विकिपीडिया
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.5
first-letter
मीडिया
विशेष
वार्तालाप
प्रयोगकर्ता
प्रयोगकर्ता वार्ता
विकिपीडिया
विकिपीडिया वार्ता
चित्र
चित्र वार्ता
मीडियाविकि
मीडियाविकि वार्ता
टेम्पलेट
टेम्पलेट वार्ता
मदद
मदद वार्ता
श्रेणी
श्रेणी वार्ता
TimedText
TimedText talk
Module
Module talk
Event
Event talk
शिक्षा
0
4952
797109
796131
2026-06-08T19:55:59Z
SM7
3953
सुधार कइल गइल
797109
wikitext
text/x-wiki
{{for2|एकेडमिक अध्ययन के बिसय|[[शिक्षाशास्त्र]]|अउरी अइसन बिसय सभ|[[शिक्षा बिज्ञान]]}}
[[चित्र:Schoolgirls in Bamozai.JPG|thumb|alt=हाथ में कापी किताब लिहले, बइठ के ऊपर देखत कई ठे लड़िकी सभ|[[अफ़ग़ानिस्तान|अफगानिस्तान]] में एगो [[इस्कूल]] में शिक्षा लेत लड़की सभ।]]
'''शिक्षा''' ({{Lang|en|education}}), ''एजुकेशन'' भा ''एडुकेशन'') अइसन क्रिया ह जेह में कौनों तरह के ज्ञान, कौशल, नैतिक मूल्य, बिस्वास, आ आदत सिखावे के ब्यवस्था कइल जाला। शिक्षा के माध्यम के रूप में इस्कूली पढ़ाई भर नइखे सामिल, बलुक खीसा-कहनी से ले के चर्चा-परिचर्चा आ बीडियो देखावल भी सामिल हो सके ला। शिक्षा अक्सरहा केहू पढ़ावे सिखावे वाला ब्यक्ति के द्वारा दिहल जाले, जेकरा के शिक्षक कहल जाला; हालाँकि, कुछ लोग बिना केहू शिक्षक के भी सीख सकत बा।<ref>{{cite book
| last = Dewey
| first = John
| title = Democracy and Education
| publisher = The Free Press
| date = 1944
| orig-year = 1916
| pages = 1–4
| isbn = 0-684-83631-9}}</ref> शिक्षा औपचारिक आ अनौपचारिक रूप से हो सके ले। मतलब कि ब्यवस्थित तरीका से शिक्षा देवे के मकसद से भी ई काम कइल जा सके आ आ ब्यक्ति अपना अनुभव से भा देख-देख के भी अपने बिचार आ ज्ञान में बदलाव ले आ के शिक्षा हासिल क सके ला।
ब्यवस्थित शिक्षा के चार भाग में बाँटल जाला, नर्सरी/किंडरगार्डेन ([[इस्कूल]] से पहिले), [[प्राइमरी इस्कूल]] में, माध्यमिक इस्कूल में, [[कॉलेज]] या [[यूनिवर्सिटी]] में या अप्रेंटिस के रूप में।
शिक्षा के अधिकार, के अरथ हवे एक ठो ख़ास उमिर के लड़िकन के शिक्षा हासिल करे के कानूनी अधिकार।<ref name="ICESCR-art13.1">''ICESCR'', Article 13.1</ref> बहुत सारा देसन में कुछ उमिर तक के लड़िका-बच्चा सभ खातिर शिक्षा अनिवार्य कइल गइल बाटे।
एगो दुसरे सेंस में, अंग्रेजी के ''एजुकेशन'' शब्द के इस्तेमाल ज्ञान के ओह शाखा भा एकेडमिक बिसय खातिर कइल जाला जे पढ़ाई आ पढ़ावे सिखावे के अध्ययन करे ला — परंपरागत रूप से एह बिज्ञान के [[शिक्षाशास्त्र]] भा ''पेडागोजी'' कहल जाला; जबकि [[शिक्षा बिज्ञान]] सभ में कई गो अकेडमिक बिसय सामिल बाड़ें।
{{clear}}
== इहो देखल जाय ==
* [[जानकारी]]
* [[विद्यालय]]
* [[शिक्षाशास्त्र]]
* [[शिक्षा बिज्ञान]]
* [[भारतीय शिक्षा मंडल]]
== संदर्भ ==
{{Reflist|35em}}
[[श्रेणी:शिक्षा]]
[[श्रेणी:शिक्षाशास्त्र]]
{{Edu-stub}}
62qdpny7iud713dg0bvlgzpil9f4yb9
विद्यापति
0
8290
797101
763944
2026-06-08T14:31:20Z
SM7
3953
बिस्तार कइल गइल, अनुबाद कइल गइल
797101
wikitext
text/x-wiki
{{Infobox writer
| name = विद्यापति
| native_name =
| native_name_lang =
| image = Stamp of India - 1965 - Colnect 371672 - Vidyapati Commemoration.jpeg
| image_size = 240px
| alt = एगो पुरुष के चित्र सोझा देखत
| caption = भारत के 1965 में जारी डाक टिकट पर बिद्यापति
| pseudonym =
| birth_date = 1352<ref name="ignca"/><ref name="britannica"/>
| birth_place = बिस्फी, मधुबनी<ref name="britannica"/> (वर्तमान में [[भारत]] के [[बिहार]] राज्य में)
| death_date = 1448<ref name="ignca"/>
| death_place = बिस्फी, मधुबनी, बिहार<ref name="britannica"/>
<!-- <br/> विद्यापति नगर (वर्तमान नाँव), वर्तमान समस्तीपुर, बिहार, भारत<br/>
[[जनकपुर]] (निर्वास में) (वर्तमान में [[नेपाल]] के [[जनकपुर]]) --><!-- ई जगह सभ के बारे में परमान मिले तब जोड़ीं -->
| resting_place = <!-- इनके दफ्नावल ना गइल, एह पैरामीटर के मत जोड़ीं -->
| occupation = [[कवी]]
| language = मैथिली, अवहट्ट, संस्कृत
| nationality = [[भारत के लोग|भारतीय]]
| period = मध्यजुग
| genre = पद, गीत
| subjects = भक्ति, शृंगार
| movement =
| notableworks = ''कीर्तिलता'', ''पदावली''
| years_active =
| module =
| website =
}}
'''विद्यापति''' (1352–1448), चाहे '''बिद्यापति''', एगो भारतीय कवी रहलें जे [[मैथिली]], [[अवहट्ट]] आ [[संस्कृत]] में आपन रचना कइलें, प्रमुख रूप से इनके मैथिली के रचना खाती जानल-मानल जाला आ मैथिली भाषा के आदि कवी आ "मैथिल कोकिल" के उपाधी दिहल जाला। कबिता के अलावा संस्कृत में गद्य लेखन के काम भी कइलेन। भक्ति आ शृंगार इनके रचना सभ के मुख्य बिसय रहल आ कबिता के रूप गीत आ पद रहल। [[शिव]]-[[पार्वती|पारबती]] आ [[राधा]]-[[कृष्ण]] दुनों इनके भक्ति वाली रचना के बिसय बनल लोग।
इनके साहित्यिक परभाव बाद के [[हिंदी भाषा|हिंदुस्तानी भाषा]], मैथिली भाषा आ [[बंगाली भाषा|बंगाली]] भाषा के साहित्य पर सीधा-सीधा, आ अप्रत्यक्ष रूप से [[नेपाली भाषा|नेपाली]], [[ओडिया]] आ [[असमिया भाषा|असमिया]] भाषा के साहित्य पर पड़ल। विद्यापति के समय अइसन रहे जेह समय साहित्य आ संपर्क के भाषा अवहट्ट रहल आ वर्तमान मैथिली, बंगाली वगैरह देसी भाषा सभ के बिकास सुरू भइल रहल, एही से इनके रचना सभ के परभाव, जे देसी बोली के साहित्य के भाषा बना के रचल गइली सऽ, एह सगरी पूरबी भाषा सभ पर परल। एही कारन बिद्यापति के भारतीय साहित्य में लगभग उहे दर्जा दिहल जाला जे इटली में [[दांते]] के भा [[इंग्लैंड]] में [[ज्यॉफ्री चॉसर|चॉसर]] के दिहल जाला।{{sfn|विद्यापति ठाकुर|1979|p=1}}
इनके मैथिलि रचना सभ प आधारित बिदापती नाच बाद के समय में बिहार-नेपाल के मिथिला क्षेत्र के खास बिधा के रूप में अस्थापित भइल। विद्यापति के मैथिली गीत सभ आज भी [[लोकगीत]] के रूप में सुनल-गावल जालें आ इनके रचना साहित्य में उच्च-कक्षा सभ में पढ़ावल जालीं।
==समय==
विद्यापति के जनम भा निधन के बारे में लिखित रूप से कुछ ना मिले ला, एही कारन इनके समय अनुमान के बिसय हवे{{sfn|शिवप्रसाद सिंह|1999|p=1}} आ कई किसिम के अनुमान कई आधार प लगावल जाला। उपलब्ध साक्ष्य के हवाला से, [[रामधारी सिंह दिनकर]] इनके जनम 1350 ईसवी के आसपास भइल होखी अइसन लिखे लें।{{sfn|रामधारी सिंह दिनकर|2008|p=11}}
डाकटर सुभद्र झा के मत के अनुसार, विद्यापति के काल 1352 ईसवी से 1448 ईसवी हवे।<ref name="ignca">{{cite web|last1=मिश्र|first1=पूनम|title=विद्यापति: कृतित्व एवं जीवन, एक परिचय|url=http://www.ignca.nic.in/coilnet/vp001.htm|website=ignca.nic.in|publisher=Indira Gandhi National Centre for the Arts|accessdate=5 मार्च 2018|language=hi}}</ref> इहे तारीख अंग्रेजी के प्रसिद्ध ज्ञानकोश ब्रिटैनिका एनसाइक्लोपीडिया प भी मिले ला।<ref name="britannica">{{cite web|title=Vidyapati: Indian writer and poet|url=https://www.britannica.com/biography/Vidyapati-Indian-writer-and-poet|website=britannica.com|publisher=एन्साइक्लोपीडिया ब्रिटैनिका (ऑनलाइन)|accessdate=5 मार्च 2018|language=en}}</ref>
सभसे बिशद बिबरन प्रस्तुत कइले बाने शिवप्रसाद सिंह, जे विद्यापति प किताब लिखलें।{{sfn|शिवप्रसाद सिंह|2007|pp=46-55}} इनके द्वारा सोझा रखल गइल बिबिध मत के तुलना अनुसार, विद्यापति के रचना ''कीर्तिलता'' में बिबरन मिले ला कि राजा गणेश्वर के निधन लक्ष्मण संवत 252 में भइल आ ई अनुमान मानल जाला कि एह समय विद्यापति के उमिर दस बारह बरिस के रहल, यानी कि इनके जनम लगभग 242 लक्ष्मण संवत में भइल होखी। समस्या ई बा कि लक्ष्मण संवत कब सुरू भइल एहू में बिबाद बा। कुछ अनुमान के मोताबिक एह परमान के आधार प विद्यापति के जनम के तिथी 1360 ईसवी के आसपास मान लिहल गइल, काहें कि लक्ष्मण संवत के सुरुआत पर अलग-अलग मत के अनुसार 1106 से 1119 ईसवी मानल जाला आ एह तरीका से गणेश्वर के निधन के तिथी 1358 से 1371 ईसवी के बिचा में ठहरे ले। हालाँकि, शिवप्रसाद सिंह ''कीर्तिलता'' के आधार पर विद्यापति के समय के निर्धारण के ठीक ना बुझे लें आ कई लोगन के मत के परिच्छा करे के बाद आपन मत देलें कि इनके जनम 1373 ईसवी के आसपास भइल होखे ई संभव बा।{{sfn|शिवप्रसाद सिंह|2007|p=51}}{{sfn|शिवप्रसाद सिंह|1999|p=194}}<ref name="ignca" />
एही तरह से इनके निधन के तिथी के बारे में भी अनुमाने लगावल जाला। लखनसेन नाँव के कवी के कविता के हवाला से अनुमान लगावे पर शिवप्रसाद सिंह बतावे लें कि एह आधार पर विद्यापति के निधन 1424 ईसवी के आसपास ठहरे ला; हालाँकि ऊ खुदे एह बारे में लिखे लें कि ई विद्यापति के अंतिम समय मानल ठीक ना बुझाला। कुछ जगह ई बिबरन मिले ला कि विद्यापति लक्ष्मण संवत 299 (1418 ईसवी, अगर लक्ष्मण संवत के सुरुआत 1119 ईसवी मानल जाय) में ''लिखनावली'' ग्रंथ पूरा कइलेन आ 309 में भागवत के एगो प्रति लिख के पूरा कइलें, यानी एह आधार प ऊ 1428 ईसवी तक जियत रहलें। एही कारण सिंह, लखनसेन के आधार पर 1424 वाल मत ठीक ना माने लें, बस एकरा के एगो मत के रूप में लोगन के सोझा रखे लें।{{sfn|शिवप्रसाद सिंह|1999|p=198}} अन्य कथा के हवाला दे के लिखे लें कि राजा शिवसिंह के निधन के बत्तीस बरिस बाद विद्यापति एगो सपना देखलें आ उनके आपन मउअत नगीचे बुझाए लागल, यानी शिवसिंह के निधन भइल 1415 में आ एह में 32 जोड़ल जाय तब विद्यापति के निधन 1447 ईसवी के कुछ समय बाद भइल होखी।{{sfn|शिवप्रसाद सिंह|1999|p=196}}<ref name="ignca" />
अउरी दूसर मत सभ में डॉ. बिमानबिहारी मजुमदार विद्यापति के जनम 1380 ईसवी आ निधन 1460 के बाद कबो भइल माने लें; नगेन्द्रनाथ गुप्त 1440 के इनके निधन तिथी माने लें आ उमेश मिश्र इनके निधन के तिथी 1466 के बाद ले माने लें।<ref name="ignca" /> हालाँकि, मजुमदार अपना बिचार में 1460 के बाद बिद्यापति के होखे के बात के खंडन करे लें। मजुमदार के मोताबिक विद्यापति के जिनगी के महत्व वाला घटना सभ के क्रम बा: 1380 के आसपास इनके जनम, 1395-96 ईसवी के आसपास पद लिख के गियासुद्दीन आ नसरत शाह के समर्पित कइल, 1397 में सुलतान जौनपुर द्वारा तिरहुत जीतल गइल जेकरे पहिले ई दुनों पद लिखल गइल रहलें; 1400 के आसपास ''भूपरिक्रमा'' के रचना, 1402-04 के बीच इब्राहिम शाह द्वारा तिरहुत के सिंघासन पर कीर्तिसिंह के स्थापित कइल आ ओही समय के आसपास ''कीर्तिलता'' के रचना; 1410 से 1414 के बीच शिवसिंह के राज्यकाल में दू सौ पद सभ के रचना; 1440 से 1460 के बीच ''विभागसागर'', ''दान-वाक्यावली'', आ ''दुर्गाभक्ति तरंगिणी'' के रचना।{{sfn|शिवप्रसाद सिंह|2007|pp=53-54}}
[[हिंदी साहित्य]] के परंपरा में, विद्यापति के समय "आदिकाल" में परे ला। आदिकाल के दूसर नाँव "वीरगाथा काल" भी हवे, हालाँकि, विद्यापति के काब्य के बीरगाथा से कवनो तालमेल ना बा, साथे-साथ ई एह काल के समाप्ति के बाद ले रहलें अइसन भी बिचार कइल जाला, यानी लोग इनके आदिकाल में रखे पर संतोख ना करे ला। आदिकाल के बाद के समय के "भक्तिकाल" मानल जाला, जबकि रामचंद्र शुक्ल इनका के भक्ति वाला कवी ना माने लें।{{sfn|रामचंद्र शुक्ल|2010|p=37}} शुक्ल जी के अइसन बिचार के कारन विद्यापति के शृंगार प्रधान रचना बा।{{efn|शुकुल जी के कहनाम कोट कइल जाला कि "विद्यापति के पद अधिकतर शृंगार के ही हैं जिनमें नायिका और नायक राधा-कृष्ण हैं। इन पदों की रचना जयदेव के गीतकाव्य के अनुकरण पर ही शायद की गई है। इनका माधुर्य अद्भूत है। विद्यापति शैव थे। इन्होंने इन पदों की रचना शृंगार काव्य की दृष्टि से की है, भक्त के रूप में नहीं। विद्यापति को कृष्णभक्तों की परंपरा में नहीं समझना चाहिये।"<ref name="dustudy">{{cite web|title=विद्यापति भक्त या शृंगारिक कवि|url=http://vle.du.ac.in/mod/book/print.php?id=12922&chapterid=27770|website=du.ac.in|publisher=दिल्ली विश्वविद्यालय|accessdate=5 मार्च 2018|language=hi}}{{Dead link|date=September 2023 |bot=InternetArchiveBot |fix-attempted=yes }}</ref>}} जबकि [[हजारी प्रसाद द्विवेदी]] शुकुल जी के बिचार के निवारण करे लें आ विद्यापति के भक्ती वाली रचना सभ के पूरबी भाषा सभ के साहित्य पर बाद में परल परभाव के ओर धियान दिवावे लें।{{efn|हजारी प्रसाद जी के हवाला दिहल जाला की, "विद्यापति शृंगार रस के सिद्धवाक कवि थे। उनकी पदावली में राधा और कृष्ण की जिस प्रेमलीला का चित्रण है, वह अपूर्व है। इस वर्णन में प्रेम के शरीर पक्ष की प्रधानता अवश्य है पर इससे सहृदय के चित्त में विकार नहीं उत्पन्न होता बल्कि भावों की सांद्रता और अभिव्यक्ति की प्रेषणगुणिता के कारण वह बहुत ही आकर्षक हो गया है। ...राधा और कृष्ण के प्रेम प्रसंगों को यह पुस्तक प्रथम बार उत्तर भारत में गेय पदों में प्रकाशित करती है। इस पुस्तक के पदों ने आगे चलकर बंगाल, असम, और उड़ीसा के वैष्णव भक्तों को खूब प्रभावित किया और उन प्रदेशों के भक्ति साहित्य में नई प्रेरणा और प्राणधारा संचारित करने में समर्थ हुई। इसीलिए पूर्वी प्रदेशों में सर्वत्र यह पुस्तक धर्म ग्रंथ की महिमा पा सकी।"<ref name="dustudy" />}} कुल मिला के विद्यापति, अपना काब्य के बिसेस्ता के आधार पर ना त आदिकाल के कवी के रूप में साबित होखे लें ना भक्ति काल के बिसेस्ता उनुका रचना में निर्बिबाद रूप से खोजल जा सके ला। दिनकर के कहनाम बा की, ''"...विद्यापति कवनो बर्ग में ना समा सके लें। उनुके सत्कार खाती अइसन सिंघासन चाही जवना प खाली उहे बइठ सके लें। ऊ खाली कबी रहलें आ कबिता में सौंदर्य आ आनंद के छाड़ के ऊ अउरी कवनो बात के जगहा ना दें।"''{{efn|''"विद्यापति किसी भी वर्ग में नहीं समा सकते। उनके सत्कार के लिए ऐसा सिंहासन चाहिए जिस पर केवल वही बैठ सकते हैं। वे केवल कवि थे और कविता में सौन्दर्य और आनन्द को छोड़ कर वे किसी और बात को स्थान नहीं देते थे।"'' रामधारी सिंह दिनकर।{{sfn|रामधारी सिंह दिनकर|2008|p=12}} }}
== रचना संसार ==
[[चित्र:Statue of Maha Kavi Kokil Vidyapati.jpg|thumb|alt=सीना से ऊपर के हिस्सा देखावत एगो मूर्ती|विद्यापति के एगो मुर्ती, बिस्फी, [[मधुबनी]], बिहार]]
=== पदावली ===
पदावली बिद्यापति के सभसे परसिद्ध रचना हवे। एह में गावल जा सके वाला सुघर पद बाड़ें। इनहन के बिसय शिव के भक्ती, राधा-कृष्ण के प्रेम, आम प्रेम आ सुंदरता के बखान बाटे।
=== अन्य रचना ===
अउरी बिबिध बिसय पर बिद्यापति कलम चलवलें जेह में मुख्य-मुख्य बाड़ें:
* ''पुरुष परीक्षा'' — मुख्य रूप से नैतिक शिक्षा आ सदाचार संबंधी उपदेशन पर आधारित बा। हाल में प्रकाशन विभाग, [[भारत सरकार]] द्वारा एह ग्रंथ के हिंदी अनुबाद प्रकाशित कइल गइल बा, जेकर अनुबाद अखिलेश झा कइले बाड़ें। एह पुस्तक में मूल रचना के 44 कहानी सभ में से चुनल गइल 25 कहानी शामिल बाड़ी स। एकरे साथे पुस्तक में विद्यापति आ पुरुष परीक्षा दुनो पर विद्वत्तापूर्ण भूमिका (परिचयात्मक लेख) भी दिहल गइल बा। पुरुष परीक्षा के प्रमुख विषय नैतिक शिक्षा, मानव चरित्र के परख आ आदर्श आचरण के मार्गदर्शन हवे।
* ''लिखनावली'' लेखन के काम के बारे में।
* ''भू-परिक्रमा'', भूगोल के बारे में, एह में आसपास के जगह वगैरह के बरनन बाटे।
* ''विभागसार'' आत्मकथा नियर रचना।
* ''दानवाक्यावली'' दान के बारे में।
* ''गंगावाक्यावली''
* ''वर्षकृत्य''
* ''दुर्गाभक्तितरंगिणी''
* ''शैवसर्वस्वहार''
* ''कीर्तिपताका''
* ''कीर्तिलता''
{{clear}}
==नोट आ टीका-टिप्पणी==
<references group="नोट"/>
==संदर्भ==
{{Reflist|35em}}
==संदर्भ ग्रंथ==
{{refbegin}}
* {{cite book|author=रामचंद्र शुक्ल |title=हिंदी साहित्य का इतिहास|url=|year=2010|publisher=लोकभारती प्रकाशन |place=इलाहाबाद}}<!-- {{sfn|रामचंद्र शुक्ल|2010|p=37}} -->
* {{cite book|author=रामधारी सिंह दिनकर|title=कवि और कविता|url=https://books.google.com/books?id=9FqZPDa6NqcC&pg=PA11|year=2008|publisher=राजकमल प्रकाशन |isbn=978-81-8031-324-0}}<!-- {{sfn|रामधारी सिंह दिनकर|2008|p=}} -->
* {{cite book|author=शिवप्रसाद सिंह|title=कीर्तिलता और अवहट्ट भाषा |url=https://books.google.com/books?id=TVkvD3EZaBIC&pg=PT209|year=1999|publisher=वाणी प्रकाशन|isbn=978-93-5000-020-5}} <!-- {{sfn|शिवप्रसाद सिंह|1999|p=}} -->
* {{cite book|author=शिवप्रसाद सिंह|title=विद्यापति |url=https://books.google.com/books?id=0eWvGQAn7NwC |year=2007 |publisher=लोकभारती प्रकाशन ( अब राजकमल प्रकाशन) |place=इलाहाबाद|isbn=}} <!-- {{sfn|शिवप्रसाद सिंह|2007|p=}} -->
* {{cite book|author=शुभकार कपूर|title=विद्यापति और उनका काव्य: महाकवि विद्यापति की काव्य-कला एवं जीवनाकृति |url=https://books.google.com/books?id=y7VHAAAAMAAJ|year=1966|publisher=गंगा पुस्तकमाला कार्यालय}}<!-- {{sfn|शुभकार कपूर|1966|p=}} -->
* {{cite book|author=मोहन लाल|title=Encyclopaedia of Indian Literature: Sasay to Zorgot|url=https://books.google.com/books?id=KnPoYxrRfc0C&pg=PA4565|year=1992|publisher=साहित्य अकादमी|isbn=978-81-260-1221-3}}<!-- {{sfn|मोहन लाल|1992|p=}} -->
* {{cite book|author=राम दयाल राकेश|title=Vidyapati, the Greatest Poet of Mithila|url=https://books.google.com/books?id=MbBIAQAAIAAJ|year=2007|publisher=Greater Janakpur Area Development Council|isbn=978-9937-2-0148-3}}<!-- {{sfn|राम दयाल राकेश|2007|p=}} -->
* {{cite book|author=विद्यापति ठाकुर|title=Vidyapati Bangiya Padabali: Songs of the Love of Radha and Krishna |trans-title=विद्यापति बंगीय पदाबली: राधा अउरी कृष्ण के परेम-गीत |url=https://books.google.com/books?id=OqVDRnEf6SEC&pg=PT3|year=1979|publisher=Library of Alexandria|isbn=978-1-4655-1475-2}}<!-- {{sfn|विद्यापति ठाकुर|1979|p=1}} -->
* {{cite book|author=राधाकृष्ण चौधरी|title=A Survey of Maithili Literature |trans-title=मैथिली साहित्य के सर्वेक्षण |url=https://books.google.com/books?id=C0f898HDLAYC&pg=PA51|year=1976|publisher=Ram Vilas Sahu|isbn=978-93-80538-36-5}} <!-- {{harv|राधाकृष्ण चौधरी|1976|p=}} -->
{{refend}}
==बाहरी कड़ी==
* [http://www.kavitakosh.org/kk/index.php?title=%E0%A4%B5%E0%A4%BF%E0%A4%A6%E0%A5%8D%E0%A4%AF%E0%A4%BE%E0%A4%AA%E0%A4%A4%E0%A4%BF विद्यापति कऽ रचना सभ] {{Webarchive|url=https://web.archive.org/web/20090504122744/http://www.kavitakosh.org/kk/index.php?title=%E0%A4%B5%E0%A4%BF%E0%A4%A6%E0%A5%8D%E0%A4%AF%E0%A4%BE%E0%A4%AA%E0%A4%A4%E0%A4%BF |date=2009-05-04 }}, कविता कोश में। {{In lang|hi}}
* [http://www.cse.iitk.ac.in/~amit/books/vidyapati-1963-love-songs-of.html 27 poems] transl. Deben Bhattacharya, from ''Love Songs of Vidyapati'', (UNESCO) 1963. {{in lang|en}}
* [https://hdl.handle.net/2027/coo1.ark:/13960/t6446799m {{IAST|Songs of the love of Rādhā and Krishna}}], translated into English by Ananda Coomaraswamy and Arun Sen 1915. {{in lang|en}}
<!-- https://www.prabhatkhabar.com/news/63307.aspx बादमें इस्तेमाल खाती -->
<!-- http://www.pupdepartments.ac.in/de/lesson/ug/ba/Semester%203/Hindi/L-%201-15%20Title.pdf बाद में इस्तेमाल खाती -->
<!-- http://www.sahityasudha.com/articles_nov_2016/lekh/nayana_deliwala/lekh_adikaleen_kaviyon.html बाद में इस्तेमाल खाती -->
{{Authority control}}
[[श्रेणी:भारतीय कवि]]
[[श्रेणी:मैथिली-भाषा के लेखक]]
[[श्रेणी:मैथिली-भाषा के कवि]]
[[श्रेणी:बिहार के लोग]]
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:15वीं-सदी के भारतीय कवि]]
[[श्रेणी:मध्यजुग के भारतीय कवि]]
3vunwu91ovh9obir8alktath3beh873
797102
797101
2026-06-08T14:32:30Z
SM7
3953
+विकिकड़ी जोड़ल गइल
797102
wikitext
text/x-wiki
{{Infobox writer
| name = विद्यापति
| native_name =
| native_name_lang =
| image = Stamp of India - 1965 - Colnect 371672 - Vidyapati Commemoration.jpeg
| image_size = 240px
| alt = एगो पुरुष के चित्र सोझा देखत
| caption = भारत के 1965 में जारी डाक टिकट पर बिद्यापति
| pseudonym =
| birth_date = 1352<ref name="ignca"/><ref name="britannica"/>
| birth_place = बिस्फी, मधुबनी<ref name="britannica"/> (वर्तमान में [[भारत]] के [[बिहार]] राज्य में)
| death_date = 1448<ref name="ignca"/>
| death_place = बिस्फी, मधुबनी, बिहार<ref name="britannica"/>
<!-- <br/> विद्यापति नगर (वर्तमान नाँव), वर्तमान समस्तीपुर, बिहार, भारत<br/>
[[जनकपुर]] (निर्वास में) (वर्तमान में [[नेपाल]] के [[जनकपुर]]) --><!-- ई जगह सभ के बारे में परमान मिले तब जोड़ीं -->
| resting_place = <!-- इनके दफ्नावल ना गइल, एह पैरामीटर के मत जोड़ीं -->
| occupation = [[कवी]]
| language = मैथिली, अवहट्ट, संस्कृत
| nationality = [[भारत के लोग|भारतीय]]
| period = मध्यजुग
| genre = पद, गीत
| subjects = [[भक्ति]], शृंगार
| movement =
| notableworks = ''कीर्तिलता'', ''पदावली''
| years_active =
| module =
| website =
}}
'''विद्यापति''' (1352–1448), चाहे '''बिद्यापति''', एगो भारतीय कवी रहलें जे [[मैथिली]], [[अवहट्ट]] आ [[संस्कृत]] में आपन रचना कइलें, प्रमुख रूप से इनके मैथिली के रचना खाती जानल-मानल जाला आ मैथिली भाषा के आदि कवी आ "मैथिल कोकिल" के उपाधी दिहल जाला। कबिता के अलावा संस्कृत में गद्य लेखन के काम भी कइलेन। [[भक्ति ]] आ शृंगार इनके रचना सभ के मुख्य बिसय रहल आ कबिता के रूप गीत आ पद रहल। [[शिव]]-[[पार्वती|पारबती]] आ [[राधा]]-[[कृष्ण]] दुनों इनके भक्ति वाली रचना के बिसय बनल लोग।
इनके साहित्यिक परभाव बाद के [[हिंदी भाषा|हिंदुस्तानी भाषा]], मैथिली भाषा आ [[बंगाली भाषा|बंगाली]] भाषा के साहित्य पर सीधा-सीधा, आ अप्रत्यक्ष रूप से [[नेपाली भाषा|नेपाली]], [[ओडिया]] आ [[असमिया भाषा|असमिया]] भाषा के साहित्य पर पड़ल। विद्यापति के समय अइसन रहे जेह समय साहित्य आ संपर्क के भाषा अवहट्ट रहल आ वर्तमान मैथिली, बंगाली वगैरह देसी भाषा सभ के बिकास सुरू भइल रहल, एही से इनके रचना सभ के परभाव, जे देसी बोली के साहित्य के भाषा बना के रचल गइली सऽ, एह सगरी पूरबी भाषा सभ पर परल। एही कारन बिद्यापति के भारतीय साहित्य में लगभग उहे दर्जा दिहल जाला जे इटली में [[दांते]] के भा [[इंग्लैंड]] में [[ज्यॉफ्री चॉसर|चॉसर]] के दिहल जाला।{{sfn|विद्यापति ठाकुर|1979|p=1}}
इनके मैथिलि रचना सभ प आधारित बिदापती नाच बाद के समय में बिहार-नेपाल के मिथिला क्षेत्र के खास बिधा के रूप में अस्थापित भइल। विद्यापति के मैथिली गीत सभ आज भी [[लोकगीत]] के रूप में सुनल-गावल जालें आ इनके रचना साहित्य में उच्च-कक्षा सभ में पढ़ावल जालीं।
==समय==
विद्यापति के जनम भा निधन के बारे में लिखित रूप से कुछ ना मिले ला, एही कारन इनके समय अनुमान के बिसय हवे{{sfn|शिवप्रसाद सिंह|1999|p=1}} आ कई किसिम के अनुमान कई आधार प लगावल जाला। उपलब्ध साक्ष्य के हवाला से, [[रामधारी सिंह दिनकर]] इनके जनम 1350 ईसवी के आसपास भइल होखी अइसन लिखे लें।{{sfn|रामधारी सिंह दिनकर|2008|p=11}}
डाकटर सुभद्र झा के मत के अनुसार, विद्यापति के काल 1352 ईसवी से 1448 ईसवी हवे।<ref name="ignca">{{cite web|last1=मिश्र|first1=पूनम|title=विद्यापति: कृतित्व एवं जीवन, एक परिचय|url=http://www.ignca.nic.in/coilnet/vp001.htm|website=ignca.nic.in|publisher=Indira Gandhi National Centre for the Arts|accessdate=5 मार्च 2018|language=hi}}</ref> इहे तारीख अंग्रेजी के प्रसिद्ध ज्ञानकोश ब्रिटैनिका एनसाइक्लोपीडिया प भी मिले ला।<ref name="britannica">{{cite web|title=Vidyapati: Indian writer and poet|url=https://www.britannica.com/biography/Vidyapati-Indian-writer-and-poet|website=britannica.com|publisher=एन्साइक्लोपीडिया ब्रिटैनिका (ऑनलाइन)|accessdate=5 मार्च 2018|language=en}}</ref>
सभसे बिशद बिबरन प्रस्तुत कइले बाने शिवप्रसाद सिंह, जे विद्यापति प किताब लिखलें।{{sfn|शिवप्रसाद सिंह|2007|pp=46-55}} इनके द्वारा सोझा रखल गइल बिबिध मत के तुलना अनुसार, विद्यापति के रचना ''कीर्तिलता'' में बिबरन मिले ला कि राजा गणेश्वर के निधन लक्ष्मण संवत 252 में भइल आ ई अनुमान मानल जाला कि एह समय विद्यापति के उमिर दस बारह बरिस के रहल, यानी कि इनके जनम लगभग 242 लक्ष्मण संवत में भइल होखी। समस्या ई बा कि लक्ष्मण संवत कब सुरू भइल एहू में बिबाद बा। कुछ अनुमान के मोताबिक एह परमान के आधार प विद्यापति के जनम के तिथी 1360 ईसवी के आसपास मान लिहल गइल, काहें कि लक्ष्मण संवत के सुरुआत पर अलग-अलग मत के अनुसार 1106 से 1119 ईसवी मानल जाला आ एह तरीका से गणेश्वर के निधन के तिथी 1358 से 1371 ईसवी के बिचा में ठहरे ले। हालाँकि, शिवप्रसाद सिंह ''कीर्तिलता'' के आधार पर विद्यापति के समय के निर्धारण के ठीक ना बुझे लें आ कई लोगन के मत के परिच्छा करे के बाद आपन मत देलें कि इनके जनम 1373 ईसवी के आसपास भइल होखे ई संभव बा।{{sfn|शिवप्रसाद सिंह|2007|p=51}}{{sfn|शिवप्रसाद सिंह|1999|p=194}}<ref name="ignca" />
एही तरह से इनके निधन के तिथी के बारे में भी अनुमाने लगावल जाला। लखनसेन नाँव के कवी के कविता के हवाला से अनुमान लगावे पर शिवप्रसाद सिंह बतावे लें कि एह आधार पर विद्यापति के निधन 1424 ईसवी के आसपास ठहरे ला; हालाँकि ऊ खुदे एह बारे में लिखे लें कि ई विद्यापति के अंतिम समय मानल ठीक ना बुझाला। कुछ जगह ई बिबरन मिले ला कि विद्यापति लक्ष्मण संवत 299 (1418 ईसवी, अगर लक्ष्मण संवत के सुरुआत 1119 ईसवी मानल जाय) में ''लिखनावली'' ग्रंथ पूरा कइलेन आ 309 में भागवत के एगो प्रति लिख के पूरा कइलें, यानी एह आधार प ऊ 1428 ईसवी तक जियत रहलें। एही कारण सिंह, लखनसेन के आधार पर 1424 वाल मत ठीक ना माने लें, बस एकरा के एगो मत के रूप में लोगन के सोझा रखे लें।{{sfn|शिवप्रसाद सिंह|1999|p=198}} अन्य कथा के हवाला दे के लिखे लें कि राजा शिवसिंह के निधन के बत्तीस बरिस बाद विद्यापति एगो सपना देखलें आ उनके आपन मउअत नगीचे बुझाए लागल, यानी शिवसिंह के निधन भइल 1415 में आ एह में 32 जोड़ल जाय तब विद्यापति के निधन 1447 ईसवी के कुछ समय बाद भइल होखी।{{sfn|शिवप्रसाद सिंह|1999|p=196}}<ref name="ignca" />
अउरी दूसर मत सभ में डॉ. बिमानबिहारी मजुमदार विद्यापति के जनम 1380 ईसवी आ निधन 1460 के बाद कबो भइल माने लें; नगेन्द्रनाथ गुप्त 1440 के इनके निधन तिथी माने लें आ उमेश मिश्र इनके निधन के तिथी 1466 के बाद ले माने लें।<ref name="ignca" /> हालाँकि, मजुमदार अपना बिचार में 1460 के बाद बिद्यापति के होखे के बात के खंडन करे लें। मजुमदार के मोताबिक विद्यापति के जिनगी के महत्व वाला घटना सभ के क्रम बा: 1380 के आसपास इनके जनम, 1395-96 ईसवी के आसपास पद लिख के गियासुद्दीन आ नसरत शाह के समर्पित कइल, 1397 में सुलतान जौनपुर द्वारा तिरहुत जीतल गइल जेकरे पहिले ई दुनों पद लिखल गइल रहलें; 1400 के आसपास ''भूपरिक्रमा'' के रचना, 1402-04 के बीच इब्राहिम शाह द्वारा तिरहुत के सिंघासन पर कीर्तिसिंह के स्थापित कइल आ ओही समय के आसपास ''कीर्तिलता'' के रचना; 1410 से 1414 के बीच शिवसिंह के राज्यकाल में दू सौ पद सभ के रचना; 1440 से 1460 के बीच ''विभागसागर'', ''दान-वाक्यावली'', आ ''दुर्गाभक्ति तरंगिणी'' के रचना।{{sfn|शिवप्रसाद सिंह|2007|pp=53-54}}
[[हिंदी साहित्य]] के परंपरा में, विद्यापति के समय "आदिकाल" में परे ला। आदिकाल के दूसर नाँव "वीरगाथा काल" भी हवे, हालाँकि, विद्यापति के काब्य के बीरगाथा से कवनो तालमेल ना बा, साथे-साथ ई एह काल के समाप्ति के बाद ले रहलें अइसन भी बिचार कइल जाला, यानी लोग इनके आदिकाल में रखे पर संतोख ना करे ला। आदिकाल के बाद के समय के "भक्तिकाल" मानल जाला, जबकि रामचंद्र शुक्ल इनका के भक्ति वाला कवी ना माने लें।{{sfn|रामचंद्र शुक्ल|2010|p=37}} शुक्ल जी के अइसन बिचार के कारन विद्यापति के शृंगार प्रधान रचना बा।{{efn|शुकुल जी के कहनाम कोट कइल जाला कि "विद्यापति के पद अधिकतर शृंगार के ही हैं जिनमें नायिका और नायक राधा-कृष्ण हैं। इन पदों की रचना जयदेव के गीतकाव्य के अनुकरण पर ही शायद की गई है। इनका माधुर्य अद्भूत है। विद्यापति शैव थे। इन्होंने इन पदों की रचना शृंगार काव्य की दृष्टि से की है, भक्त के रूप में नहीं। विद्यापति को कृष्णभक्तों की परंपरा में नहीं समझना चाहिये।"<ref name="dustudy">{{cite web|title=विद्यापति भक्त या शृंगारिक कवि|url=http://vle.du.ac.in/mod/book/print.php?id=12922&chapterid=27770|website=du.ac.in|publisher=दिल्ली विश्वविद्यालय|accessdate=5 मार्च 2018|language=hi}}{{Dead link|date=September 2023 |bot=InternetArchiveBot |fix-attempted=yes }}</ref>}} जबकि [[हजारी प्रसाद द्विवेदी]] शुकुल जी के बिचार के निवारण करे लें आ विद्यापति के भक्ती वाली रचना सभ के पूरबी भाषा सभ के साहित्य पर बाद में परल परभाव के ओर धियान दिवावे लें।{{efn|हजारी प्रसाद जी के हवाला दिहल जाला की, "विद्यापति शृंगार रस के सिद्धवाक कवि थे। उनकी पदावली में राधा और कृष्ण की जिस प्रेमलीला का चित्रण है, वह अपूर्व है। इस वर्णन में प्रेम के शरीर पक्ष की प्रधानता अवश्य है पर इससे सहृदय के चित्त में विकार नहीं उत्पन्न होता बल्कि भावों की सांद्रता और अभिव्यक्ति की प्रेषणगुणिता के कारण वह बहुत ही आकर्षक हो गया है। ...राधा और कृष्ण के प्रेम प्रसंगों को यह पुस्तक प्रथम बार उत्तर भारत में गेय पदों में प्रकाशित करती है। इस पुस्तक के पदों ने आगे चलकर बंगाल, असम, और उड़ीसा के वैष्णव भक्तों को खूब प्रभावित किया और उन प्रदेशों के भक्ति साहित्य में नई प्रेरणा और प्राणधारा संचारित करने में समर्थ हुई। इसीलिए पूर्वी प्रदेशों में सर्वत्र यह पुस्तक धर्म ग्रंथ की महिमा पा सकी।"<ref name="dustudy" />}} कुल मिला के विद्यापति, अपना काब्य के बिसेस्ता के आधार पर ना त आदिकाल के कवी के रूप में साबित होखे लें ना भक्ति काल के बिसेस्ता उनुका रचना में निर्बिबाद रूप से खोजल जा सके ला। दिनकर के कहनाम बा की, ''"...विद्यापति कवनो बर्ग में ना समा सके लें। उनुके सत्कार खाती अइसन सिंघासन चाही जवना प खाली उहे बइठ सके लें। ऊ खाली कबी रहलें आ कबिता में सौंदर्य आ आनंद के छाड़ के ऊ अउरी कवनो बात के जगहा ना दें।"''{{efn|''"विद्यापति किसी भी वर्ग में नहीं समा सकते। उनके सत्कार के लिए ऐसा सिंहासन चाहिए जिस पर केवल वही बैठ सकते हैं। वे केवल कवि थे और कविता में सौन्दर्य और आनन्द को छोड़ कर वे किसी और बात को स्थान नहीं देते थे।"'' रामधारी सिंह दिनकर।{{sfn|रामधारी सिंह दिनकर|2008|p=12}} }}
== रचना संसार ==
[[चित्र:Statue of Maha Kavi Kokil Vidyapati.jpg|thumb|alt=सीना से ऊपर के हिस्सा देखावत एगो मूर्ती|विद्यापति के एगो मुर्ती, बिस्फी, [[मधुबनी]], बिहार]]
=== पदावली ===
पदावली बिद्यापति के सभसे परसिद्ध रचना हवे। एह में गावल जा सके वाला सुघर पद बाड़ें। इनहन के बिसय शिव के भक्ती, राधा-कृष्ण के प्रेम, आम प्रेम आ सुंदरता के बखान बाटे।
=== अन्य रचना ===
अउरी बिबिध बिसय पर बिद्यापति कलम चलवलें जेह में मुख्य-मुख्य बाड़ें:
* ''पुरुष परीक्षा'' — मुख्य रूप से नैतिक शिक्षा आ सदाचार संबंधी उपदेशन पर आधारित बा। हाल में प्रकाशन विभाग, [[भारत सरकार]] द्वारा एह ग्रंथ के हिंदी अनुबाद प्रकाशित कइल गइल बा, जेकर अनुबाद अखिलेश झा कइले बाड़ें। एह पुस्तक में मूल रचना के 44 कहानी सभ में से चुनल गइल 25 कहानी शामिल बाड़ी स। एकरे साथे पुस्तक में विद्यापति आ पुरुष परीक्षा दुनो पर विद्वत्तापूर्ण भूमिका (परिचयात्मक लेख) भी दिहल गइल बा। पुरुष परीक्षा के प्रमुख विषय नैतिक शिक्षा, मानव चरित्र के परख आ आदर्श आचरण के मार्गदर्शन हवे।
* ''लिखनावली'' लेखन के काम के बारे में।
* ''भू-परिक्रमा'', भूगोल के बारे में, एह में आसपास के जगह वगैरह के बरनन बाटे।
* ''विभागसार'' आत्मकथा नियर रचना।
* ''दानवाक्यावली'' दान के बारे में।
* ''गंगावाक्यावली''
* ''वर्षकृत्य''
* ''दुर्गाभक्तितरंगिणी''
* ''शैवसर्वस्वहार''
* ''कीर्तिपताका''
* ''कीर्तिलता''
{{clear}}
==नोट आ टीका-टिप्पणी==
<references group="नोट"/>
==संदर्भ==
{{Reflist|35em}}
==संदर्भ ग्रंथ==
{{refbegin}}
* {{cite book|author=रामचंद्र शुक्ल |title=हिंदी साहित्य का इतिहास|url=|year=2010|publisher=लोकभारती प्रकाशन |place=इलाहाबाद}}<!-- {{sfn|रामचंद्र शुक्ल|2010|p=37}} -->
* {{cite book|author=रामधारी सिंह दिनकर|title=कवि और कविता|url=https://books.google.com/books?id=9FqZPDa6NqcC&pg=PA11|year=2008|publisher=राजकमल प्रकाशन |isbn=978-81-8031-324-0}}<!-- {{sfn|रामधारी सिंह दिनकर|2008|p=}} -->
* {{cite book|author=शिवप्रसाद सिंह|title=कीर्तिलता और अवहट्ट भाषा |url=https://books.google.com/books?id=TVkvD3EZaBIC&pg=PT209|year=1999|publisher=वाणी प्रकाशन|isbn=978-93-5000-020-5}} <!-- {{sfn|शिवप्रसाद सिंह|1999|p=}} -->
* {{cite book|author=शिवप्रसाद सिंह|title=विद्यापति |url=https://books.google.com/books?id=0eWvGQAn7NwC |year=2007 |publisher=लोकभारती प्रकाशन ( अब राजकमल प्रकाशन) |place=इलाहाबाद|isbn=}} <!-- {{sfn|शिवप्रसाद सिंह|2007|p=}} -->
* {{cite book|author=शुभकार कपूर|title=विद्यापति और उनका काव्य: महाकवि विद्यापति की काव्य-कला एवं जीवनाकृति |url=https://books.google.com/books?id=y7VHAAAAMAAJ|year=1966|publisher=गंगा पुस्तकमाला कार्यालय}}<!-- {{sfn|शुभकार कपूर|1966|p=}} -->
* {{cite book|author=मोहन लाल|title=Encyclopaedia of Indian Literature: Sasay to Zorgot|url=https://books.google.com/books?id=KnPoYxrRfc0C&pg=PA4565|year=1992|publisher=साहित्य अकादमी|isbn=978-81-260-1221-3}}<!-- {{sfn|मोहन लाल|1992|p=}} -->
* {{cite book|author=राम दयाल राकेश|title=Vidyapati, the Greatest Poet of Mithila|url=https://books.google.com/books?id=MbBIAQAAIAAJ|year=2007|publisher=Greater Janakpur Area Development Council|isbn=978-9937-2-0148-3}}<!-- {{sfn|राम दयाल राकेश|2007|p=}} -->
* {{cite book|author=विद्यापति ठाकुर|title=Vidyapati Bangiya Padabali: Songs of the Love of Radha and Krishna |trans-title=विद्यापति बंगीय पदाबली: राधा अउरी कृष्ण के परेम-गीत |url=https://books.google.com/books?id=OqVDRnEf6SEC&pg=PT3|year=1979|publisher=Library of Alexandria|isbn=978-1-4655-1475-2}}<!-- {{sfn|विद्यापति ठाकुर|1979|p=1}} -->
* {{cite book|author=राधाकृष्ण चौधरी|title=A Survey of Maithili Literature |trans-title=मैथिली साहित्य के सर्वेक्षण |url=https://books.google.com/books?id=C0f898HDLAYC&pg=PA51|year=1976|publisher=Ram Vilas Sahu|isbn=978-93-80538-36-5}} <!-- {{harv|राधाकृष्ण चौधरी|1976|p=}} -->
{{refend}}
==बाहरी कड़ी==
* [http://www.kavitakosh.org/kk/index.php?title=%E0%A4%B5%E0%A4%BF%E0%A4%A6%E0%A5%8D%E0%A4%AF%E0%A4%BE%E0%A4%AA%E0%A4%A4%E0%A4%BF विद्यापति कऽ रचना सभ] {{Webarchive|url=https://web.archive.org/web/20090504122744/http://www.kavitakosh.org/kk/index.php?title=%E0%A4%B5%E0%A4%BF%E0%A4%A6%E0%A5%8D%E0%A4%AF%E0%A4%BE%E0%A4%AA%E0%A4%A4%E0%A4%BF |date=2009-05-04 }}, कविता कोश में। {{In lang|hi}}
* [http://www.cse.iitk.ac.in/~amit/books/vidyapati-1963-love-songs-of.html 27 poems] transl. Deben Bhattacharya, from ''Love Songs of Vidyapati'', (UNESCO) 1963. {{in lang|en}}
* [https://hdl.handle.net/2027/coo1.ark:/13960/t6446799m {{IAST|Songs of the love of Rādhā and Krishna}}], translated into English by Ananda Coomaraswamy and Arun Sen 1915. {{in lang|en}}
<!-- https://www.prabhatkhabar.com/news/63307.aspx बादमें इस्तेमाल खाती -->
<!-- http://www.pupdepartments.ac.in/de/lesson/ug/ba/Semester%203/Hindi/L-%201-15%20Title.pdf बाद में इस्तेमाल खाती -->
<!-- http://www.sahityasudha.com/articles_nov_2016/lekh/nayana_deliwala/lekh_adikaleen_kaviyon.html बाद में इस्तेमाल खाती -->
{{Authority control}}
[[श्रेणी:भारतीय कवि]]
[[श्रेणी:मैथिली-भाषा के लेखक]]
[[श्रेणी:मैथिली-भाषा के कवि]]
[[श्रेणी:बिहार के लोग]]
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:15वीं-सदी के भारतीय कवि]]
[[श्रेणी:मध्यजुग के भारतीय कवि]]
dm4bicnjpfuaio0lzyxtr45koc5nhh2
रबींद्रनाथ टैगोर
0
8658
797601
796936
2026-06-09T08:04:41Z
अजीत कुमार तिवारी
4590
हिज्जे/इस्पेलिंग सुधार
797601
wikitext
text/x-wiki
{{Infobox writer
| name = रबींद्रनाथ ठाकुर
| native_name = রবীন্দ্রনাথ ঠাকুর
| image = Tagore3.jpg
| image_size = 225
| alt = Late-middle-aged bearded man in white robes looks to the left with serene composure.
| caption = रबींद्रनाथ ठाकुर (c.1915)
| birth_name = Rabindranath Thakur
| birth_date = {{Birth date|df=yes|1861|05|07}}
| birth_place = [[कलकत्ता]], [[बंगाल प्रेसीडेंसी]], [[ब्रिटिश भारत]]
| death_date = {{Death date and age|df=yes|1941|08|07|1861|05|07}}
| death_place = कलकत्ता
| occupation = कवि, लेखक, चित्रकार, संगीतकार
| language = [[बंगाली भाषा|बंगाली]], अंग्रेजी
| nationality =
| education =
| alma_mater =
| period =
| genre =
| subject =
| movement =
| notableworks = ''[[गीतांजली]]'', ''गोरा'', ''[[घरे-बाहिरे]]'', ''[[जन गण मन]]'', ''[[रबीन्द्र संगीत]]'', ''[[आमार सोनार बांग्ला]]''
| influences =
| spouse = {{marriage|मृणालिनी देवी|1883|1902}}
| children = पाँच गो संतान, दू के बचपन में मउत हो गइल
| relatives =
| awards = {{awd|[[साहित्य के नोबल पुरस्कार]]|1913}}<!-- do not add image icons such as nobel peace, see [[:Template:Infobox writer]] -->
| signature = Rabindranath Tagore Signature.svg
| signature_alt = Close-up on a Bengali word handwritten with angular, jaunty letters.
}}
'''रबींद्रनाथ ठाकुर''' ({{Langx|bn|রবীন্দ্রনাথ ঠাকুর}}, रोबिंद्रोनाथ ठाकुर) (7 मई 1861 — 7 अगस्त 1941) के '''रबींद्रनाथ टैगोर''' आऊर '''गुरुदेव''' के नाम से भी जानल जाला। रबींद्रनाथ दुनिया क जानल मानल कवि, साहित्यकार, दार्शनिक आ भारतीय साहित्य क एकमात्र [[नोबल पुरस्कार]] बिजेता हउवन। ई [[एशिया]] के पहिला [[नोबेल पुरस्कार]] सम्मानित व्यक्ति बाटें। ई एकलौता कवि बाटें जिनकर दू रचना दू गो देशन क राष्ट्रगान बा — [[भारत]] क राष्ट्रगान ''[[जन गण मन]]'' आ [[बाँग्लादेश]] क राष्ट्रीय गान ''आमार सोनार बाँग्ला'' ठाकुर क ही रचना बाड़ीं।
{{clear}}
== बाहरी कड़ी ==
* [http://www.nobel.se/literature/laureates/1913/tagore-bio.html रबींद्रनाथ, जीवनी, Nobel Foundation]
* [http://www.nobel.se/literature/laureates/1913/press.html Nobel Prize in Literature Presentation Speech from the official website of Nobel Foundation]
{{Authority control}}
[[श्रेणी:1961 में जनम]]
[[श्रेणी:1941 में निधन]]
[[श्रेणी:नोबेल पुरस्कार विजेता]]
[[श्रेणी:साहित्यकार]]
[[श्रेणी:बांग्ला साहित्यकार]]
[[श्रेणी:नोबेल पुरस्कार सम्मानित भारतीय]]
[[श्रेणी:लेखक]]
[[श्रेणी:भारत के लोग]]
[[श्रेणी:अंग्रेजी-भाषा के भारतीय कवि]]
{{poet-stub}}
dfi6ti5ofyjt70w2ducp2fyw8hopfdl
वार्तालाप:रबींद्रनाथ टैगोर
1
8983
797600
673037
2026-06-09T07:59:52Z
अजीत कुमार तिवारी
4590
/* नाँव बदलाव अनुरोध 9 जून 2026 */ नया खंड
797600
wikitext
text/x-wiki
{{वार्ता शीर्षक}}
{{WikiProject Biography
| living= no
| class= Stub
| authorbio-work-group = yes
}}
== नाँव बदलाव अनुरोध 9 जून 2026 ==
{{requested move/dated|रबींद्रनाथ ठाकुर}}
[[:रबींद्रनाथ टैगोर]] → {{no redirect|रबींद्रनाथ ठाकुर}} – मूल बांग्ला में ठाकुर कहल जाला। [[User:अजीत कुमार तिवारी|<span style="text-shadow:gray 3px 3px 2px;color:red">'''अजीत कुमार तिवारी'''</span>]]<sup>[[User talk:अजीत कुमार तिवारी|<span style="color:green"> '''बातचीत'''</span>]]</sup> 07:59, 9 जून 2026 (UTC)
r3l0bp5tfkd1vbnzy70pdwd417g6p1d
कबीर
0
9246
797602
790505
2026-06-09T08:54:34Z
SM7
3953
बिना प्रमाणिक स्रोत संदर्भ
797602
wikitext
text/x-wiki
[[Image:Kabir004.jpg|thumbnail|An 1825 CE painting depicts Kabir weaving|right]]
'''कबीरदास''' चाहे संत '''कबीर'''<ref>{{cite book|author=Jaroslav Strnad|title=Morphology and Syntax of Old Hindī: Edition and Analysis of One Hundred Kabīr vānī Poems from Rājasthān|url=https://books.google.com/books?id=clUCLcIKXO4C&pg=PA10 |year=2013|publisher=BRILL Academic|isbn=978-90-04-25489-3|page=10}}</ref> 15वीं सदी के एगो संत महात्मा, रहस्यवादी आ कवी जिनके रचना आ उपदेश के परभाव, कुछ बिद्वान लोग के अनुसार तत्कालीन भक्ति आंदोलन पर पड़ल। इनकर जनम [[बनारस]] की लगे लहरतारा में भइल रहे। धार्मिक पाखण्ड की खिलाफ उपदेश दिहलें आ भगवान की निर्गुण रूप के आराधना करे के उपदेश दिहलें। निर्गुण भक्ति की [[कवि]] लोगन में कबीरदास क अस्थान बहुत ऊपर बा। कबीर के रचना सभ के अंश [[सिख धर्म]] के पबित्र ग्रंथ [[गुरु ग्रंथ साहिब]] में भी शामिल मिले ला।<ref name=britannicakabir>[http://www.britannica.com/EBchecked/topic/309270/Kabir Kabir] Encyclopædia Britannica (2015)Accessed: 27 जुलाई 2015</ref><ref name="Tinker1990">{{cite book|author=Hugh Tinker|title=South Asia: A Short History|url=https://books.google.com/books?id=n5uU2UteUpEC&pg=PA76|accessdate=12 जुलाई 2012|year=1990|publisher=University of Hawaii Press|isbn=978-0-8248-1287-4|pages=75–77}}</ref> उ जुलाहा के काम करके निर्वाह करत रहले।
कबीर साहेब जी के प्रकट धरती पर भारत वर्ष के पावन भूमि काशी में भईल रहे। कबीर सागर के अनुसार उहा के सशरीर प्रकट सन 1398 (संवत 1455), में ज्येष्ठ मास की पूर्णिमा के ब्रह्ममूहर्त के समय लहरतारा तालाब में कमल के फूल पर भईल रहे । जहां से नीरू नीमा नामक दंपति उठा के ले गइल रहले। उनके इ लीला के उनके अनुयायी कबीर साहेब प्रकट दिवस के रूप में मनावेलालो। बाद में ई हिंदू संत आ गुरू रामानंद के परभाव में अइलें आ उनके चेला बन गइलें।<ref>{{Cite web|url=https://www.britannica.com/biography/Kabir-Indian-mystic-and-poet|title=Kabir|last=Encyclopedia Brittanica|first=The Editors of|date=1 जनवरी 2019|website=Encyclopedia Brittanica|accessdate=20 जनवरी 2019}}</ref>
कबीर के हिंदू आ [[इस्लाम|मुस्लिम]] दुनों धरम के आलोचना करे खाती जानल जाला। उनके अनुसार हिंदू लोग वेद से आ करमकांड से भरमाव में बा आ ऊ एह लोग के धार्मिक रेवाज सभ के भरपूर आलोचना कइलें जेह में जनेव आ खतना दुनों के बिरोध सामिल बा।<ref name="GarciaHenderson2002">{{cite book|author1=Carol Henderson Garcia|author2=Carol E. Henderson|title=Culture and Customs of India|url=https://books.google.com/books?id=CaRVePXX6vEC&pg=PA70|accessdate=12 जुलाई 2012|year=2002|publisher=Greenwood Publishing Group|isbn=978-0-313-30513-9|pages=70–71}}</ref> इनका जिनगी में हिंदू आ मुस्लिम दुनों धरम के लोग इनके बिचार खातिर इनका के धमकावल। निधन के बाद इनके ऊपर दुनों धरम के लोग दावा कइल।<ref name="Tinker1990"/>(एह बात पर बिबाद भइल कि इनके जरावल जाय कि दफन कइल जाव)।
== कृतियां ==
धर्मदास उनके वाणी क संग्रह " बीजक " नाम के ग्रंथ मे कइन जेकर तीन मुख्य भाग ह: साखी , सबद (पद), रमैनी
*''' साखी''': संस्कृत ' साक्षी , शब्द क बिगड़ल रूप ह आ धर्मोपदेश के अर्थ में प्रयोग भयल ह। अधिक साखियां दोहे में लिखल गयल ह पर ओम्में सोरठा क भी प्रयोग मिलल ह। कबीर क शिक्षा और सिद्धांत क निरूपण अधिकतर साखी में भयल ह।
*'''सबद ''' गेय पद ह जेम्मा पूरी तरह संगीतात्मकता विद्यमान ह। एम्में उपदेशात्मकता के स्थान पर भावावेश क प्रधानता ह; काहें से कि एम्में कबीर के प्रेम आ अंतरंग साधना क अभिव्यक्ति भइल ह।
*'''रमैनी ''' चौपाई छंद में लिखल गयल ह एम्में कबीर के रहस्यवादी आ दार्शनिक विचारन के बतावल गयल ह।
[[File:Kabir-stamp-370x630.jpg|thumb|Kabir-stamp-370x630]]
{{clear}}
==संदर्भ==
{{Reflist|33em}}
[[श्रेणी:भक्ति आंदोलन]]
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:बनारस के लोग]]
[[श्रेणी:बनारस जिला के लोग]]
{{कवि-आधार}}
q2xj9d4677ddyrekko2t0ywgc69tclr
अर्थशास्त्र
0
15078
797558
780327
2026-06-08T21:06:11Z
SM7
3953
सुधार कइल गइल
797558
wikitext
text/x-wiki
{{for|कौटिल्य द्वारा रचित ग्रंथ|अर्थशास्त्रम्}}
[[चित्र:Supply-demand-right-shift-demand.svg|240px|thumb|alt=डिमांड आ सप्लाई ग्राफ| सप्लाई आ डिमांड के डाइग्राम; मांग में बढ़ती के परभाव देखा रहल बा।]]
'''अर्थशास्त्र''' ({{Lang|en|Economics}}; ''इकोनॉमिक्स'') [[सामाजिक विज्ञान]] के साखा हऽ जेकरा भीतर चीजन आ सेवा कुल के उत्पादन, वितरन, बिनिमय आ उपभोग के अधयन करल जाएला। 'अर्थशास्त्र' सबद [[संस्कृत]] सबद अर्थ (धन) आ शास्त्र के जोड़ से बनल बा, जेकर ठेत अर्थ होखी - 'धन के अधयन'। कवनो बिषय से जुड़ला में मुनष्यन के काजन के ज्ञान के उ बिषय के शास्त्र कहल जाएला, इहे खातिर अर्थशास्त्र में इंसान के अरथ से जुड़ल काजन के ज्ञान होखल जरूरी बा।
अर्थशास्त्र के फोकस एह बात पर होला कि ढेर तरीका के[[एजेंट (अर्थशास्त्र)|आर्थिक एजेंट]] सभ के बेहवार आ आपसी तालमेल कवना तरीका के बा आ [[अर्थब्यवस्था|अरथ बेवसथा]] सभ कइसे काम करे लीं। [[माइक्रोइकोनॉमिक्स]] में अर्थब्यवस्था के बेसिक तत्व सभ के बिस्लेषण कइल जाला जेह में एकहन एजेंट आ बजार (मार्केट), इनहन के अंतर्क्रिया, आ एह अंतर्क्रिया (इंटरैक्शन) सभ के परिणाम के अध्ययन शामिल होला। एह एकहक ठो एजेंट सभ में परिवार, फर्म, बिक्रेता, खरीदार वगैरह लोग सामिल होला।
एकरे बिपरीत [[मैक्रोइकोनॉमिक्स]] में पूरा अर्थब्यवस्था के बिस्लेषण कइल जाला (मने कि पूरा संपूर्ण उत्पादन, उपभोग, बचत, आ निवेश) आ पूरा अर्थब्यवस्था के परभावित करे वाला मुद्दा, [[बेरोजगारी]], बिबिध किसिम के आर्थिक नीति वगैरह के अध्ययन आ बिस्लेषण कइल जाला।
अर्थशास्त्रीय विवेचना के प्रयोग समाज से संबंधित विभिन्न क्षेत्रन में कइल जायेला, जैसे:- अपराध, शिक्षा, परिवार, स्वास्थ्य, कानून, राजनीति, धर्म, सामाजिक संस्थान और युद्ध इत्यदि।
{{Clear}}
==संदर्भ==
{{Reflist}}
{{Economics}}
{{Authority control}}
[[श्रेणी:अर्थशास्त्र]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:एकैडमिक बिसय]]
{{बिज्ञान-आधार}}
5270ppzievx2o05r5lk3jxnvj6w9fam
797559
797558
2026-06-08T22:29:50Z
SM7
3953
सुधार कइल गइल
797559
wikitext
text/x-wiki
{{for|कौटिल्य द्वारा रचित ग्रंथ|अर्थशास्त्रम्}}
[[चित्र:Supply-demand-right-shift-demand.svg|240px|thumb|alt=डिमांड आ सप्लाई ग्राफ| सप्लाई आ डिमांड के डाइग्राम; मांग में बढ़ती के परभाव देखा रहल बा।]]
'''अर्थशास्त्र''' ({{Lang|en|Economics}}; ''इकोनॉमिक्स'') [[सामाजिक विज्ञान]] के साखा हऽ जेकरा भीतर बस्तु आ सेवा (गुड्स एंड सर्विसेस) कुल के उत्पादन, बितरन, बिनिमय आ उपभोग के अधयन करल जाएला। 'अर्थशास्त्र' शब्द [[संस्कृत]] के 'अर्थ' (माने [[धन]]) आ 'शास्त्र' के जोड़ से बनल बा, जेकर ठेत अर्थ होखी - 'धन के अधयन'। कवनो बिषय से जुड़ला में मुनष्यन के काजन के ज्ञान के उ बिषय के शास्त्र कहल जाएला, इहे खातिर अर्थशास्त्र में इंसान के अरथ से जुड़ल काजन के ज्ञान होखल जरूरी बा।
अर्थशास्त्र के फोकस एह बात पर होला कि ढेर तरीका के[[एजेंट (अर्थशास्त्र)|आर्थिक एजेंट]] सभ के बेहवार आ आपसी तालमेल कवना तरीका के बा आ [[अर्थब्यवस्था|अरथ बेवसथा]] सभ कइसे काम करे लीं। [[माइक्रोइकोनॉमिक्स]] में अर्थब्यवस्था के बेसिक तत्व सभ के बिस्लेषण कइल जाला जेह में एकहन एजेंट आ बजार (मार्केट), इनहन के अंतर्क्रिया, आ एह अंतर्क्रिया (इंटरैक्शन) सभ के परिणाम के अध्ययन शामिल होला। एह एकहक ठो एजेंट सभ में परिवार, फर्म, बिक्रेता, खरीदार वगैरह लोग सामिल होला।
एकरे बिपरीत [[मैक्रोइकोनॉमिक्स]] में पूरा अर्थब्यवस्था के बिस्लेषण कइल जाला (मने कि पूरा संपूर्ण उत्पादन, उपभोग, बचत, आ निवेश) आ पूरा अर्थब्यवस्था के परभावित करे वाला मुद्दा, [[बेरोजगारी]], बिबिध किसिम के आर्थिक नीति वगैरह के अध्ययन आ बिस्लेषण कइल जाला।
अर्थशास्त्रीय विवेचना के प्रयोग समाज से संबंधित विभिन्न क्षेत्रन में कइल जायेला, जैसे:- अपराध, शिक्षा, परिवार, स्वास्थ्य, कानून, राजनीति, धर्म, सामाजिक संस्थान और युद्ध इत्यदि।
{{Clear}}
==संदर्भ==
{{Reflist}}
{{Economics}}
{{Authority control}}
[[श्रेणी:अर्थशास्त्र]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:एकैडमिक बिसय]]
{{बिज्ञान-आधार}}
i88am4h8tl5rv1y3rz07fcaank3inf9
797560
797559
2026-06-08T22:47:30Z
SM7
3953
बिस्तार कइल गइल
797560
wikitext
text/x-wiki
{{for|कौटिल्य द्वारा रचित ग्रंथ|अर्थशास्त्रम्}}
[[चित्र:Supply-demand-right-shift-demand.svg|240px|thumb|alt=डिमांड आ सप्लाई ग्राफ| सप्लाई आ डिमांड के डाइग्राम; मांग में बढ़ती के परभाव देखा रहल बा।]]
'''अर्थशास्त्र''' ({{Lang|en|Economics}}; ''इकोनॉमिक्स'') [[सामाजिक विज्ञान]] के साखा हऽ जेकरा भीतर बस्तु आ सेवा (गुड्स एंड सर्विसेस) कुल के उत्पादन, बितरन, बिनिमय आ उपभोग के अधयन करल जाएला। 'अर्थशास्त्र' शब्द [[संस्कृत]] के 'अर्थ' (माने [[धन]]) आ 'शास्त्र' के जोड़ से बनल बा, जेकर ठेत अर्थ होखी - 'धन के अधयन'। कवनो बिषय से जुड़ला में मुनष्यन के काजन के ज्ञान के उ बिषय के शास्त्र कहल जाएला, इहे खातिर अर्थशास्त्र में इंसान के अरथ से जुड़ल काजन के ज्ञान होखल जरूरी बा।
अर्थशास्त्र के फोकस एह बात पर होला कि ढेर तरीका के[[एजेंट (अर्थशास्त्र)|आर्थिक एजेंट]] सभ के बेहवार आ आपसी तालमेल कवना तरीका के बा आ [[अर्थब्यवस्था|अरथ बेवसथा]] सभ कइसे काम करे लीं। [[माइक्रोइकोनॉमिक्स]] में अर्थब्यवस्था के बेसिक तत्व सभ के बिस्लेषण कइल जाला जेह में एकहन एजेंट आ बजार (मार्केट), इनहन के अंतर्क्रिया, आ एह अंतर्क्रिया (इंटरैक्शन) सभ के परिणाम के अध्ययन शामिल होला। एह एकहक ठो एजेंट सभ में परिवार, फर्म, बिक्रेता, खरीदार वगैरह लोग सामिल होला।
एकरे बिपरीत [[मैक्रोइकोनॉमिक्स]] में पूरा अर्थब्यवस्था के बिस्लेषण कइल जाला (मने कि पूरा संपूर्ण उत्पादन, उपभोग, बचत, आ निवेश) आ पूरा अर्थब्यवस्था के परभावित करे वाला मुद्दा, [[बेरोजगारी]], बिबिध किसिम के आर्थिक नीति वगैरह के अध्ययन आ बिस्लेषण कइल जाला।
अर्थशास्त्रीय विवेचना के प्रयोग समाज से संबंधित विभिन्न क्षेत्रन में कइल जायेला, जैसे:- अपराध, शिक्षा, परिवार, स्वास्थ्य, कानून, राजनीति, धर्म, सामाजिक संस्थान और युद्ध इत्यदि।
== माइक्रो इकोनॉमिक्स ==
{{main|माइक्रोइकोनॉमिक्स}}
माइक्रोइकोनॉमिक्स ({{Langx|en|व्यष्टि अर्थशास्त्र}}) एह बात के अध्ययन करे ला कि बजार ढाँचा के हिस्सा बने वाली अलग-अलग इकाई (एंटिटी) बजार के भीतर एक-दूसरा के साथ कइसे संपर्क करे लीं आ आपस में मिल के केङ्ने बजार बेवस्था के निर्माण करे लीं। एह इकाई सभ में निजी आ सार्वजनिक दुनो तरह के भागीदार शामिल हो सकेलें। ई सभ आमतौर पर सीमित संसाधन (स्कार्सिटी) आ नियमन (रेगुलेशन) के परिस्थिति में काम करेलन। बजार में खरीद-बिक्री होखे वाला वस्तु कवनो ठोस उत्पाद, जइसे सेब, भा कवनो सेवा, जइसे मरम्मत सेवा, कानूनी सलाह भा मनोरंजन सेवा, हो सकेला।
अर्थशास्त्र में कई तरह के बाजार संरचना पावल जाली। [[परफेक्ट कंपटीशन]] (पूर्ण प्रतियोगिता) वाला बजार में कवनो भागीदार एतना बड़हन ना होला कि ऊ अकेले कवनो समान प्रकार के उत्पाद के दाम तय कर सके। एह स्थिति में सभे भागीदार प्राइस टेकर (दाम स्वीकार करे वाला) होलें, काहे कि कवनो सिंगल बिक्रेता प्रोडक्ट के दाम पर प्रभाव ना डाल सके ला। हालाँकि, असल दुनिया में अधिकतर बजार परफेक्ट कंपटीशन वाला ना होके इम्परफेक्ट कंपटीशन वाला होलें।
इम्परफेक्ट कंपटीशन के कई रूप बाड़ें। एकाधिकार (मोनोपोली) में कवनो वस्तु के खाली एक्के ठो बिक्रेता होला। द्वयाधिकार (डुओपोली) में दू गो बिक्रेता होलें, जबकि ओलिगोपोली में बिक्रेता लोगन के गिनती कम होला। मोनोपोलिस्टिक कंपटीशन में बहुत बिक्रेता होखे लें, बाकी ऊ सभ अलग-अलग बिसेस्ता वाला प्रोडक्ट बेचेलन। मोनोपसनी में खाली एके गो खरीदार होला, जबकि ओलिगोपसनी में खरीददारन के संख्या कम रहेला। इम्परफेक्ट कंपटीशन वाला बाजार में काम करे वाली फर्म सभ के अक्सर प्राइस मेकर (दाम निश्चित करे वाला) बने के क्षमता होले। मतलब, ऊ अपना उत्पाद के दाम पर कुछ हद तक प्रभाव डाल सकेली आ ओकरा के निर्धारित करे में भूमिका निभा सके लीं।
{{Clear}}
==संदर्भ==
{{Reflist}}
{{Economics}}
{{Authority control}}
[[श्रेणी:अर्थशास्त्र]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:एकैडमिक बिसय]]
{{बिज्ञान-आधार}}
bkhu4gq3cd1qoq08xwfa4re29126lwm
797561
797560
2026-06-08T22:52:21Z
SM7
3953
बिस्तार कइल गइल
797561
wikitext
text/x-wiki
{{for|कौटिल्य द्वारा रचित ग्रंथ|अर्थशास्त्रम्}}
[[चित्र:Supply-demand-right-shift-demand.svg|240px|thumb|alt=डिमांड आ सप्लाई ग्राफ| सप्लाई आ डिमांड के डाइग्राम; मांग में बढ़ती के परभाव देखा रहल बा।]]
'''अर्थशास्त्र''' ({{Lang|en|Economics}}; ''इकोनॉमिक्स'') [[सामाजिक विज्ञान]] के साखा हऽ जेकरा भीतर बस्तु आ सेवा (गुड्स एंड सर्विसेस) कुल के उत्पादन, बितरन, बिनिमय आ उपभोग के अधयन करल जाएला। 'अर्थशास्त्र' शब्द [[संस्कृत]] के 'अर्थ' (माने [[धन]]) आ 'शास्त्र' के जोड़ से बनल बा, जेकर ठेत अर्थ होखी - 'धन के अधयन'। कवनो बिषय से जुड़ला में मुनष्यन के काजन के ज्ञान के उ बिषय के शास्त्र कहल जाएला, इहे खातिर अर्थशास्त्र में इंसान के अरथ से जुड़ल काजन के ज्ञान होखल जरूरी बा।
अर्थशास्त्र के फोकस एह बात पर होला कि ढेर तरीका के[[एजेंट (अर्थशास्त्र)|आर्थिक एजेंट]] सभ के बेहवार आ आपसी तालमेल कवना तरीका के बा आ [[अर्थब्यवस्था|अरथ बेवसथा]] सभ कइसे काम करे लीं। [[माइक्रोइकोनॉमिक्स]] में अर्थब्यवस्था के बेसिक तत्व सभ के बिस्लेषण कइल जाला जेह में एकहन एजेंट आ बजार (मार्केट), इनहन के अंतर्क्रिया, आ एह अंतर्क्रिया (इंटरैक्शन) सभ के परिणाम के अध्ययन शामिल होला। एह एकहक ठो एजेंट सभ में परिवार, फर्म, बिक्रेता, खरीदार वगैरह लोग सामिल होला।
एकरे बिपरीत [[मैक्रोइकोनॉमिक्स]] में पूरा अर्थब्यवस्था के बिस्लेषण कइल जाला (मने कि पूरा संपूर्ण उत्पादन, उपभोग, बचत, आ निवेश) आ पूरा अर्थब्यवस्था के परभावित करे वाला मुद्दा, [[बेरोजगारी]], बिबिध किसिम के आर्थिक नीति वगैरह के अध्ययन आ बिस्लेषण कइल जाला।
अर्थशास्त्रीय विवेचना के प्रयोग समाज से संबंधित विभिन्न क्षेत्रन में कइल जायेला, जैसे:- अपराध, शिक्षा, परिवार, स्वास्थ्य, कानून, राजनीति, धर्म, सामाजिक संस्थान और युद्ध इत्यदि।
== माइक्रो इकोनॉमिक्स ==
{{main|माइक्रोइकोनॉमिक्स}}
माइक्रोइकोनॉमिक्स ({{Langx|en|व्यष्टि अर्थशास्त्र}}) एह बात के अध्ययन करे ला कि बजार ढाँचा के हिस्सा बने वाली अलग-अलग इकाई (एंटिटी) बजार के भीतर एक-दूसरा के साथ कइसे संपर्क करे लीं आ आपस में मिल के केङ्ने बजार बेवस्था के निर्माण करे लीं। एह इकाई सभ में निजी आ सार्वजनिक दुनो तरह के भागीदार शामिल हो सकेलें। ई सभ आमतौर पर सीमित संसाधन (स्कार्सिटी) आ नियमन (रेगुलेशन) के परिस्थिति में काम करेलन। बजार में खरीद-बिक्री होखे वाला वस्तु कवनो ठोस उत्पाद, जइसे सेब, भा कवनो सेवा, जइसे मरम्मत सेवा, कानूनी सलाह भा मनोरंजन सेवा, हो सकेला।
अर्थशास्त्र में कई तरह के बाजार संरचना पावल जाली। [[परफेक्ट कंपटीशन]] (पूर्ण प्रतियोगिता) वाला बजार में कवनो भागीदार एतना बड़हन ना होला कि ऊ अकेले कवनो समान प्रकार के उत्पाद के दाम तय कर सके। एह स्थिति में सभे भागीदार प्राइस टेकर (दाम स्वीकार करे वाला) होलें, काहे कि कवनो सिंगल बिक्रेता प्रोडक्ट के दाम पर प्रभाव ना डाल सके ला। हालाँकि, असल दुनिया में अधिकतर बजार परफेक्ट कंपटीशन वाला ना होके इम्परफेक्ट कंपटीशन वाला होलें।
इम्परफेक्ट कंपटीशन के कई रूप बाड़ें। एकाधिकार (मोनोपोली) में कवनो वस्तु के खाली एक्के ठो बिक्रेता होला। द्वयाधिकार (डुओपोली) में दू गो बिक्रेता होलें, जबकि ओलिगोपोली में बिक्रेता लोगन के गिनती कम होला। मोनोपोलिस्टिक कंपटीशन में बहुत बिक्रेता होखे लें, बाकी ऊ सभ अलग-अलग बिसेस्ता वाला प्रोडक्ट बेचेलन। मोनोपसनी में खाली एके गो खरीदार होला, जबकि ओलिगोपसनी में खरीददारन के संख्या कम रहेला। इम्परफेक्ट कंपटीशन वाला बाजार में काम करे वाली फर्म सभ के अक्सर प्राइस मेकर (दाम निश्चित करे वाला) बने के क्षमता होले। मतलब, ऊ अपना उत्पाद के दाम पर कुछ हद तक प्रभाव डाल सकेली आ ओकरा के निर्धारित करे में भूमिका निभा सके लीं।
पार्शियल इक्विलिब्रियम (आंशिक संतुलन) विश्लेषण सिस्टम में ई मान के चलल जाला कि जवन बजार के अध्ययन कइल जा रहल बा, ओकर गतिविधि के दुसरे बजारन पर कवनो प्रभाव नइखे पड़त। एह तरीका में एनालिसिस खाली एकही बजार तक सीमित रहे ला आ ओही बाजार के कुल गतिविधियन के आधार पर निष्कर्ष निकालल जाला।एकरा विपरीत, जनरल इक्विलिब्रियम थ्योरी एक्के संघे कई बजारन आ उनहन के बेहवार के अध्ययन करेला। ई तरीका सगरी बजारन के कुल गतिविधियन के एकजुट रूप में देखे ला आ एह बात के जाँच करेला कि अलग-अलग बजारन में होखे वाला बदलाव एक-दूसरा पर कइसे प्रभाव डाले लें। एह किसिम के एनालिसिस में बजारन के आपसी संबंध आ परस्पर क्रिया (इंतारैक्शन) के अध्ययन कइल जाला, जवना के परिणामस्वरूप अर्थव्यवस्था में संतुलन (इक्विलिब्रियम) स्थापित होखे ला।
{{Clear}}
==संदर्भ==
{{Reflist}}
{{Economics}}
{{Authority control}}
[[श्रेणी:अर्थशास्त्र]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:एकैडमिक बिसय]]
{{बिज्ञान-आधार}}
llbqb1ow0elt7n0lxuifsygwa2enhp8
797562
797561
2026-06-08T23:17:47Z
SM7
3953
बिस्तार कइल गइल
797562
wikitext
text/x-wiki
{{for|कौटिल्य द्वारा रचित ग्रंथ|अर्थशास्त्रम्}}
[[चित्र:Supply-demand-right-shift-demand.svg|240px|thumb|alt=डिमांड आ सप्लाई ग्राफ| सप्लाई आ डिमांड के डाइग्राम; मांग में बढ़ती के परभाव देखा रहल बा।]]
'''अर्थशास्त्र''' ({{Lang|en|Economics}}; ''इकोनॉमिक्स'') [[सामाजिक विज्ञान]] के साखा हऽ जेकरा भीतर बस्तु आ सेवा (गुड्स एंड सर्विसेस) कुल के उत्पादन, बितरन, बिनिमय आ उपभोग के अधयन करल जाएला। 'अर्थशास्त्र' शब्द [[संस्कृत]] के 'अर्थ' (माने [[धन]]) आ 'शास्त्र' के जोड़ से बनल बा, जेकर ठेत अर्थ होखी - 'धन के अधयन'। कवनो बिषय से जुड़ला में मुनष्यन के काजन के ज्ञान के उ बिषय के शास्त्र कहल जाएला, इहे खातिर अर्थशास्त्र में इंसान के अरथ से जुड़ल काजन के ज्ञान होखल जरूरी बा।
अर्थशास्त्र के फोकस एह बात पर होला कि ढेर तरीका के[[एजेंट (अर्थशास्त्र)|आर्थिक एजेंट]] सभ के बेहवार आ आपसी तालमेल कवना तरीका के बा आ [[अर्थब्यवस्था|अरथ बेवसथा]] सभ कइसे काम करे लीं। [[माइक्रोइकोनॉमिक्स]] में अर्थब्यवस्था के बेसिक तत्व सभ के बिस्लेषण कइल जाला जेह में एकहन एजेंट आ बजार (मार्केट), इनहन के अंतर्क्रिया, आ एह अंतर्क्रिया (इंटरैक्शन) सभ के परिणाम के अध्ययन शामिल होला। एह एकहक ठो एजेंट सभ में परिवार, फर्म, बिक्रेता, खरीदार वगैरह लोग सामिल होला।
एकरे बिपरीत [[मैक्रोइकोनॉमिक्स]] में पूरा अर्थब्यवस्था के बिस्लेषण कइल जाला (मने कि पूरा संपूर्ण उत्पादन, उपभोग, बचत, आ निवेश) आ पूरा अर्थब्यवस्था के परभावित करे वाला मुद्दा, [[बेरोजगारी]], बिबिध किसिम के आर्थिक नीति वगैरह के अध्ययन आ बिस्लेषण कइल जाला।
अर्थशास्त्रीय विवेचना के प्रयोग समाज से संबंधित विभिन्न क्षेत्रन में कइल जायेला, जैसे:- अपराध, शिक्षा, परिवार, स्वास्थ्य, कानून, राजनीति, धर्म, सामाजिक संस्थान और युद्ध इत्यदि।
== माइक्रो इकोनॉमिक्स ==
{{main|माइक्रोइकोनॉमिक्स}}
माइक्रोइकोनॉमिक्स ({{Langx|hi|व्यष्टि अर्थशास्त्र}}) एह बात के अध्ययन करे ला कि बजार ढाँचा के हिस्सा बने वाली अलग-अलग इकाई (एंटिटी) बजार के भीतर एक-दूसरा के साथ कइसे संपर्क करे लीं आ आपस में मिल के केङ्ने बजार बेवस्था के निर्माण करे लीं। एह इकाई सभ में निजी आ सार्वजनिक दुनो तरह के भागीदार शामिल हो सकेलें। ई सभ आमतौर पर सीमित संसाधन (स्कार्सिटी) आ नियमन (रेगुलेशन) के परिस्थिति में काम करेलन। बजार में खरीद-बिक्री होखे वाला वस्तु कवनो ठोस उत्पाद, जइसे सेब, भा कवनो सेवा, जइसे मरम्मत सेवा, कानूनी सलाह भा मनोरंजन सेवा, हो सकेला।
अर्थशास्त्र में कई तरह के बाजार संरचना पावल जाली। [[परफेक्ट कंपटीशन]] (पूर्ण प्रतियोगिता) वाला बजार में कवनो भागीदार एतना बड़हन ना होला कि ऊ अकेले कवनो समान प्रकार के उत्पाद के दाम तय कर सके। एह स्थिति में सभे भागीदार प्राइस टेकर (दाम स्वीकार करे वाला) होलें, काहे कि कवनो सिंगल बिक्रेता प्रोडक्ट के दाम पर प्रभाव ना डाल सके ला। हालाँकि, असल दुनिया में अधिकतर बजार परफेक्ट कंपटीशन वाला ना होके इम्परफेक्ट कंपटीशन वाला होलें।
इम्परफेक्ट कंपटीशन के कई रूप बाड़ें। एकाधिकार (मोनोपोली) में कवनो वस्तु के खाली एक्के ठो बिक्रेता होला। द्वयाधिकार (डुओपोली) में दू गो बिक्रेता होलें, जबकि ओलिगोपोली में बिक्रेता लोगन के गिनती कम होला। मोनोपोलिस्टिक कंपटीशन में बहुत बिक्रेता होखे लें, बाकी ऊ सभ अलग-अलग बिसेस्ता वाला प्रोडक्ट बेचेलन। मोनोपसनी में खाली एके गो खरीदार होला, जबकि ओलिगोपसनी में खरीददारन के संख्या कम रहेला। इम्परफेक्ट कंपटीशन वाला बाजार में काम करे वाली फर्म सभ के अक्सर प्राइस मेकर (दाम निश्चित करे वाला) बने के क्षमता होले। मतलब, ऊ अपना उत्पाद के दाम पर कुछ हद तक प्रभाव डाल सकेली आ ओकरा के निर्धारित करे में भूमिका निभा सके लीं।
पार्शियल इक्विलिब्रियम (आंशिक संतुलन) विश्लेषण सिस्टम में ई मान के चलल जाला कि जवन बजार के अध्ययन कइल जा रहल बा, ओकर गतिविधि के दुसरे बजारन पर कवनो प्रभाव नइखे पड़त। एह तरीका में एनालिसिस खाली एकही बजार तक सीमित रहे ला आ ओही बाजार के कुल गतिविधियन के आधार पर निष्कर्ष निकालल जाला।एकरा विपरीत, जनरल इक्विलिब्रियम थ्योरी एक्के संघे कई बजारन आ उनहन के बेहवार के अध्ययन करेला। ई तरीका सगरी बजारन के कुल गतिविधियन के एकजुट रूप में देखे ला आ एह बात के जाँच करेला कि अलग-अलग बजारन में होखे वाला बदलाव एक-दूसरा पर कइसे प्रभाव डाले लें। एह किसिम के एनालिसिस में बजारन के आपसी संबंध आ परस्पर क्रिया (इंतारैक्शन) के अध्ययन कइल जाला, जवना के परिणामस्वरूप अर्थव्यवस्था में संतुलन (इक्विलिब्रियम) स्थापित होखे ला।
== मैक्रोइकोनॉमिक्स ==
मैक्रोइकोनॉमिक्स ({{Langx|hi|समष्टि अर्थशास्त्र}}) अर्थशास्त्र के एगो दूसर प्रमुख शाखा हवे, जवन पूरा [[अर्थव्यवस्था]] के समग्र रूप में अध्ययन करे ला। ई व्यापक आर्थिक सूचकन आ उनहन के परस्पर संबंधन के ऊपर से नीचे (टॉप-डाउन) दृष्टिकोण से समझे-समझावे के कोसिस करे ला, आ आमतौर पर जनरल इक्विलिब्रियम थियरी के सरल रूप के इस्तेमाल करेला। मैक्रोइकोनॉमिक्स में राष्ट्रीय आय, राष्ट्रीय उत्पादन, बेरोजगारी दर, मुद्रास्फीति (महँगाई) जइसन व्यापक आर्थिक सूचकन के अध्ययन कइल जाला। एकरे साथे कुल उपभोग, कुल निवेश व्यय आ उनका अलग-अलग घटकन के भी विश्लेषण कइल जाला। ई शाखा [[मॉनिटरी पॉलिसी]] आ [[फिस्कल पॉलिसी]] (राजकोषीय नीति) के अर्थव्यवस्था पर पड़े वाला प्रभाव सभ के भी अध्ययन करेला।
1960 के दशक से मैक्रोइकोनॉमिक्स में सूक्ष्म आर्थिक आधार पर मॉडल विकसित करे पर अधिक जोर दिहल जाए लागल। एहमें आर्थिक भागीदारन के तर्कसंगत व्यवहार, बाजार सूचना के प्रभावी उपयोग आ अपूर्ण प्रतियोगिता जइसन पहलुअन के शामिल कइल गइल। एह विकास से मैक्रोइकोनॉमिक्स आ माइक्रोइकोनॉमिक्स के बीच के कई पुरान सैद्धांतिक असंगतियन के दूर करे के कोशिश भइल। मैक्रोइकोनॉमिक एनालिसिस में ओह कारकन के भी अध्ययन कइल जाला जे राष्ट्रीय आय के दीर्घकालिक स्तर आ [[आर्थिक विकास]] के प्रभावित करेलें। एहमें पूँजी संचय, तकनीकी प्रगति आ श्रम शक्ति के वृद्धि प्रमुख कारक मानल जालें।
{{Clear}}
==संदर्भ==
{{Reflist}}
{{Economics}}
{{Authority control}}
[[श्रेणी:अर्थशास्त्र]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:एकैडमिक बिसय]]
{{बिज्ञान-आधार}}
75g4qpfq9v1hr16s8k83sjpgvv63sw1
797563
797562
2026-06-08T23:18:23Z
SM7
3953
/* मैक्रोइकोनॉमिक्स */ सुधार कइल गइल
797563
wikitext
text/x-wiki
{{for|कौटिल्य द्वारा रचित ग्रंथ|अर्थशास्त्रम्}}
[[चित्र:Supply-demand-right-shift-demand.svg|240px|thumb|alt=डिमांड आ सप्लाई ग्राफ| सप्लाई आ डिमांड के डाइग्राम; मांग में बढ़ती के परभाव देखा रहल बा।]]
'''अर्थशास्त्र''' ({{Lang|en|Economics}}; ''इकोनॉमिक्स'') [[सामाजिक विज्ञान]] के साखा हऽ जेकरा भीतर बस्तु आ सेवा (गुड्स एंड सर्विसेस) कुल के उत्पादन, बितरन, बिनिमय आ उपभोग के अधयन करल जाएला। 'अर्थशास्त्र' शब्द [[संस्कृत]] के 'अर्थ' (माने [[धन]]) आ 'शास्त्र' के जोड़ से बनल बा, जेकर ठेत अर्थ होखी - 'धन के अधयन'। कवनो बिषय से जुड़ला में मुनष्यन के काजन के ज्ञान के उ बिषय के शास्त्र कहल जाएला, इहे खातिर अर्थशास्त्र में इंसान के अरथ से जुड़ल काजन के ज्ञान होखल जरूरी बा।
अर्थशास्त्र के फोकस एह बात पर होला कि ढेर तरीका के[[एजेंट (अर्थशास्त्र)|आर्थिक एजेंट]] सभ के बेहवार आ आपसी तालमेल कवना तरीका के बा आ [[अर्थब्यवस्था|अरथ बेवसथा]] सभ कइसे काम करे लीं। [[माइक्रोइकोनॉमिक्स]] में अर्थब्यवस्था के बेसिक तत्व सभ के बिस्लेषण कइल जाला जेह में एकहन एजेंट आ बजार (मार्केट), इनहन के अंतर्क्रिया, आ एह अंतर्क्रिया (इंटरैक्शन) सभ के परिणाम के अध्ययन शामिल होला। एह एकहक ठो एजेंट सभ में परिवार, फर्म, बिक्रेता, खरीदार वगैरह लोग सामिल होला।
एकरे बिपरीत [[मैक्रोइकोनॉमिक्स]] में पूरा अर्थब्यवस्था के बिस्लेषण कइल जाला (मने कि पूरा संपूर्ण उत्पादन, उपभोग, बचत, आ निवेश) आ पूरा अर्थब्यवस्था के परभावित करे वाला मुद्दा, [[बेरोजगारी]], बिबिध किसिम के आर्थिक नीति वगैरह के अध्ययन आ बिस्लेषण कइल जाला।
अर्थशास्त्रीय विवेचना के प्रयोग समाज से संबंधित विभिन्न क्षेत्रन में कइल जायेला, जैसे:- अपराध, शिक्षा, परिवार, स्वास्थ्य, कानून, राजनीति, धर्म, सामाजिक संस्थान और युद्ध इत्यदि।
== माइक्रो इकोनॉमिक्स ==
{{main|माइक्रोइकोनॉमिक्स}}
माइक्रोइकोनॉमिक्स ({{Langx|hi|व्यष्टि अर्थशास्त्र}}) एह बात के अध्ययन करे ला कि बजार ढाँचा के हिस्सा बने वाली अलग-अलग इकाई (एंटिटी) बजार के भीतर एक-दूसरा के साथ कइसे संपर्क करे लीं आ आपस में मिल के केङ्ने बजार बेवस्था के निर्माण करे लीं। एह इकाई सभ में निजी आ सार्वजनिक दुनो तरह के भागीदार शामिल हो सकेलें। ई सभ आमतौर पर सीमित संसाधन (स्कार्सिटी) आ नियमन (रेगुलेशन) के परिस्थिति में काम करेलन। बजार में खरीद-बिक्री होखे वाला वस्तु कवनो ठोस उत्पाद, जइसे सेब, भा कवनो सेवा, जइसे मरम्मत सेवा, कानूनी सलाह भा मनोरंजन सेवा, हो सकेला।
अर्थशास्त्र में कई तरह के बाजार संरचना पावल जाली। [[परफेक्ट कंपटीशन]] (पूर्ण प्रतियोगिता) वाला बजार में कवनो भागीदार एतना बड़हन ना होला कि ऊ अकेले कवनो समान प्रकार के उत्पाद के दाम तय कर सके। एह स्थिति में सभे भागीदार प्राइस टेकर (दाम स्वीकार करे वाला) होलें, काहे कि कवनो सिंगल बिक्रेता प्रोडक्ट के दाम पर प्रभाव ना डाल सके ला। हालाँकि, असल दुनिया में अधिकतर बजार परफेक्ट कंपटीशन वाला ना होके इम्परफेक्ट कंपटीशन वाला होलें।
इम्परफेक्ट कंपटीशन के कई रूप बाड़ें। एकाधिकार (मोनोपोली) में कवनो वस्तु के खाली एक्के ठो बिक्रेता होला। द्वयाधिकार (डुओपोली) में दू गो बिक्रेता होलें, जबकि ओलिगोपोली में बिक्रेता लोगन के गिनती कम होला। मोनोपोलिस्टिक कंपटीशन में बहुत बिक्रेता होखे लें, बाकी ऊ सभ अलग-अलग बिसेस्ता वाला प्रोडक्ट बेचेलन। मोनोपसनी में खाली एके गो खरीदार होला, जबकि ओलिगोपसनी में खरीददारन के संख्या कम रहेला। इम्परफेक्ट कंपटीशन वाला बाजार में काम करे वाली फर्म सभ के अक्सर प्राइस मेकर (दाम निश्चित करे वाला) बने के क्षमता होले। मतलब, ऊ अपना उत्पाद के दाम पर कुछ हद तक प्रभाव डाल सकेली आ ओकरा के निर्धारित करे में भूमिका निभा सके लीं।
पार्शियल इक्विलिब्रियम (आंशिक संतुलन) विश्लेषण सिस्टम में ई मान के चलल जाला कि जवन बजार के अध्ययन कइल जा रहल बा, ओकर गतिविधि के दुसरे बजारन पर कवनो प्रभाव नइखे पड़त। एह तरीका में एनालिसिस खाली एकही बजार तक सीमित रहे ला आ ओही बाजार के कुल गतिविधियन के आधार पर निष्कर्ष निकालल जाला।एकरा विपरीत, जनरल इक्विलिब्रियम थ्योरी एक्के संघे कई बजारन आ उनहन के बेहवार के अध्ययन करेला। ई तरीका सगरी बजारन के कुल गतिविधियन के एकजुट रूप में देखे ला आ एह बात के जाँच करेला कि अलग-अलग बजारन में होखे वाला बदलाव एक-दूसरा पर कइसे प्रभाव डाले लें। एह किसिम के एनालिसिस में बजारन के आपसी संबंध आ परस्पर क्रिया (इंतारैक्शन) के अध्ययन कइल जाला, जवना के परिणामस्वरूप अर्थव्यवस्था में संतुलन (इक्विलिब्रियम) स्थापित होखे ला।
== मैक्रोइकोनॉमिक्स ==
{{main|मैक्रोइकोनॉमिक्स}}
मैक्रोइकोनॉमिक्स ({{Langx|hi|समष्टि अर्थशास्त्र}}) अर्थशास्त्र के एगो दूसर प्रमुख शाखा हवे, जवन पूरा [[अर्थव्यवस्था]] के समग्र रूप में अध्ययन करे ला। ई व्यापक आर्थिक सूचकन आ उनहन के परस्पर संबंधन के ऊपर से नीचे (टॉप-डाउन) दृष्टिकोण से समझे-समझावे के कोसिस करे ला, आ आमतौर पर जनरल इक्विलिब्रियम थियरी के सरल रूप के इस्तेमाल करेला। मैक्रोइकोनॉमिक्स में राष्ट्रीय आय, राष्ट्रीय उत्पादन, बेरोजगारी दर, मुद्रास्फीति (महँगाई) जइसन व्यापक आर्थिक सूचकन के अध्ययन कइल जाला। एकरे साथे कुल उपभोग, कुल निवेश व्यय आ उनका अलग-अलग घटकन के भी विश्लेषण कइल जाला। ई शाखा [[मॉनिटरी पॉलिसी]] आ [[फिस्कल पॉलिसी]] (राजकोषीय नीति) के अर्थव्यवस्था पर पड़े वाला प्रभाव सभ के भी अध्ययन करेला।
1960 के दशक से मैक्रोइकोनॉमिक्स में सूक्ष्म आर्थिक आधार पर मॉडल विकसित करे पर अधिक जोर दिहल जाए लागल। एहमें आर्थिक भागीदारन के तर्कसंगत व्यवहार, बाजार सूचना के प्रभावी उपयोग आ अपूर्ण प्रतियोगिता जइसन पहलुअन के शामिल कइल गइल। एह विकास से मैक्रोइकोनॉमिक्स आ माइक्रोइकोनॉमिक्स के बीच के कई पुरान सैद्धांतिक असंगतियन के दूर करे के कोशिश भइल। मैक्रोइकोनॉमिक एनालिसिस में ओह कारकन के भी अध्ययन कइल जाला जे राष्ट्रीय आय के दीर्घकालिक स्तर आ [[आर्थिक विकास]] के प्रभावित करेलें। एहमें पूँजी संचय, तकनीकी प्रगति आ श्रम शक्ति के वृद्धि प्रमुख कारक मानल जालें।
{{Clear}}
==संदर्भ==
{{Reflist}}
{{Economics}}
{{Authority control}}
[[श्रेणी:अर्थशास्त्र]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:एकैडमिक बिसय]]
{{बिज्ञान-आधार}}
8ueudyyweytphs01b5xn1mk9o13g1hn
797564
797563
2026-06-08T23:36:37Z
SM7
3953
बिस्तार कइल गइल
797564
wikitext
text/x-wiki
{{for|कौटिल्य द्वारा रचित ग्रंथ|अर्थशास्त्रम्}}
[[चित्र:Supply-demand-right-shift-demand.svg|240px|thumb|alt=डिमांड आ सप्लाई ग्राफ| सप्लाई आ डिमांड के डाइग्राम; मांग में बढ़ती के परभाव देखा रहल बा।]]
'''अर्थशास्त्र''' ({{Lang|en|Economics}}; ''इकोनॉमिक्स'') [[सामाजिक विज्ञान]] के साखा हऽ जेकरा भीतर बस्तु आ सेवा (गुड्स एंड सर्विसेस) कुल के उत्पादन, बितरन, बिनिमय आ उपभोग के अधयन करल जाएला। 'अर्थशास्त्र' शब्द [[संस्कृत]] के 'अर्थ' (माने [[धन]]) आ 'शास्त्र' के जोड़ से बनल बा, जेकर ठेत अर्थ होखी - 'धन के अधयन'। कवनो बिषय से जुड़ला में मुनष्यन के काजन के ज्ञान के उ बिषय के शास्त्र कहल जाएला, इहे खातिर अर्थशास्त्र में इंसान के अरथ से जुड़ल काजन के ज्ञान होखल जरूरी बा।
अर्थशास्त्र के फोकस एह बात पर होला कि ढेर तरीका के[[एजेंट (अर्थशास्त्र)|आर्थिक एजेंट]] सभ के बेहवार आ आपसी तालमेल कवना तरीका के बा आ [[अर्थब्यवस्था|अरथ बेवसथा]] सभ कइसे काम करे लीं। [[माइक्रोइकोनॉमिक्स]] में अर्थब्यवस्था के बेसिक तत्व सभ के बिस्लेषण कइल जाला जेह में एकहन एजेंट आ बजार (मार्केट), इनहन के अंतर्क्रिया, आ एह अंतर्क्रिया (इंटरैक्शन) सभ के परिणाम के अध्ययन शामिल होला। एह एकहक ठो एजेंट सभ में परिवार, फर्म, बिक्रेता, खरीदार वगैरह लोग सामिल होला।
एकरे बिपरीत [[मैक्रोइकोनॉमिक्स]] में पूरा अर्थब्यवस्था के बिस्लेषण कइल जाला (मने कि पूरा संपूर्ण उत्पादन, उपभोग, बचत, आ निवेश) आ पूरा अर्थब्यवस्था के परभावित करे वाला मुद्दा, [[बेरोजगारी]], बिबिध किसिम के आर्थिक नीति वगैरह के अध्ययन आ बिस्लेषण कइल जाला।
अर्थशास्त्रीय विवेचना के प्रयोग समाज से संबंधित विभिन्न क्षेत्रन में कइल जायेला, जैसे:- अपराध, शिक्षा, परिवार, स्वास्थ्य, कानून, राजनीति, धर्म, सामाजिक संस्थान और युद्ध इत्यदि।
== माइक्रो इकोनॉमिक्स ==
{{main|माइक्रोइकोनॉमिक्स}}
माइक्रोइकोनॉमिक्स ({{Langx|hi|व्यष्टि अर्थशास्त्र}}) एह बात के अध्ययन करे ला कि बजार ढाँचा के हिस्सा बने वाली अलग-अलग इकाई (एंटिटी) बजार के भीतर एक-दूसरा के साथ कइसे संपर्क करे लीं आ आपस में मिल के केङ्ने बजार बेवस्था के निर्माण करे लीं। एह इकाई सभ में निजी आ सार्वजनिक दुनो तरह के भागीदार शामिल हो सकेलें। ई सभ आमतौर पर सीमित संसाधन (स्कार्सिटी) आ नियमन (रेगुलेशन) के परिस्थिति में काम करेलन। बजार में खरीद-बिक्री होखे वाला वस्तु कवनो ठोस उत्पाद, जइसे सेब, भा कवनो सेवा, जइसे मरम्मत सेवा, कानूनी सलाह भा मनोरंजन सेवा, हो सकेला।
अर्थशास्त्र में कई तरह के बाजार संरचना पावल जाली। [[परफेक्ट कंपटीशन]] (पूर्ण प्रतियोगिता) वाला बजार में कवनो भागीदार एतना बड़हन ना होला कि ऊ अकेले कवनो समान प्रकार के उत्पाद के दाम तय कर सके। एह स्थिति में सभे भागीदार प्राइस टेकर (दाम स्वीकार करे वाला) होलें, काहे कि कवनो सिंगल बिक्रेता प्रोडक्ट के दाम पर प्रभाव ना डाल सके ला। हालाँकि, असल दुनिया में अधिकतर बजार परफेक्ट कंपटीशन वाला ना होके इम्परफेक्ट कंपटीशन वाला होलें।
इम्परफेक्ट कंपटीशन के कई रूप बाड़ें। एकाधिकार (मोनोपोली) में कवनो वस्तु के खाली एक्के ठो बिक्रेता होला। द्वयाधिकार (डुओपोली) में दू गो बिक्रेता होलें, जबकि ओलिगोपोली में बिक्रेता लोगन के गिनती कम होला। मोनोपोलिस्टिक कंपटीशन में बहुत बिक्रेता होखे लें, बाकी ऊ सभ अलग-अलग बिसेस्ता वाला प्रोडक्ट बेचेलन। मोनोपसनी में खाली एके गो खरीदार होला, जबकि ओलिगोपसनी में खरीददारन के संख्या कम रहेला। इम्परफेक्ट कंपटीशन वाला बाजार में काम करे वाली फर्म सभ के अक्सर प्राइस मेकर (दाम निश्चित करे वाला) बने के क्षमता होले। मतलब, ऊ अपना उत्पाद के दाम पर कुछ हद तक प्रभाव डाल सकेली आ ओकरा के निर्धारित करे में भूमिका निभा सके लीं।
पार्शियल इक्विलिब्रियम (आंशिक संतुलन) विश्लेषण सिस्टम में ई मान के चलल जाला कि जवन बजार के अध्ययन कइल जा रहल बा, ओकर गतिविधि के दुसरे बजारन पर कवनो प्रभाव नइखे पड़त। एह तरीका में एनालिसिस खाली एकही बजार तक सीमित रहे ला आ ओही बाजार के कुल गतिविधियन के आधार पर निष्कर्ष निकालल जाला।एकरा विपरीत, जनरल इक्विलिब्रियम थ्योरी एक्के संघे कई बजारन आ उनहन के बेहवार के अध्ययन करेला। ई तरीका सगरी बजारन के कुल गतिविधियन के एकजुट रूप में देखे ला आ एह बात के जाँच करेला कि अलग-अलग बजारन में होखे वाला बदलाव एक-दूसरा पर कइसे प्रभाव डाले लें। एह किसिम के एनालिसिस में बजारन के आपसी संबंध आ परस्पर क्रिया (इंतारैक्शन) के अध्ययन कइल जाला, जवना के परिणामस्वरूप अर्थव्यवस्था में संतुलन (इक्विलिब्रियम) स्थापित होखे ला।
== मैक्रोइकोनॉमिक्स ==
{{main|मैक्रोइकोनॉमिक्स}}
मैक्रोइकोनॉमिक्स ({{Langx|hi|समष्टि अर्थशास्त्र}}) अर्थशास्त्र के एगो दूसर प्रमुख शाखा हवे, जवन पूरा [[अर्थव्यवस्था]] के समग्र रूप में अध्ययन करे ला। ई व्यापक आर्थिक सूचकन आ उनहन के परस्पर संबंधन के ऊपर से नीचे (टॉप-डाउन) दृष्टिकोण से समझे-समझावे के कोसिस करे ला, आ आमतौर पर जनरल इक्विलिब्रियम थियरी के सरल रूप के इस्तेमाल करेला। मैक्रोइकोनॉमिक्स में राष्ट्रीय आय, राष्ट्रीय उत्पादन, बेरोजगारी दर, मुद्रास्फीति (महँगाई) जइसन व्यापक आर्थिक सूचकन के अध्ययन कइल जाला। एकरे साथे कुल उपभोग, कुल निवेश व्यय आ उनका अलग-अलग घटकन के भी विश्लेषण कइल जाला। ई शाखा [[मॉनिटरी पॉलिसी]] आ [[फिस्कल पॉलिसी]] (राजकोषीय नीति) के अर्थव्यवस्था पर पड़े वाला प्रभाव सभ के भी अध्ययन करेला।
1960 के दशक से मैक्रोइकोनॉमिक्स में सूक्ष्म आर्थिक आधार पर मॉडल विकसित करे पर अधिक जोर दिहल जाए लागल। एहमें आर्थिक भागीदारन के तर्कसंगत व्यवहार, बाजार सूचना के प्रभावी उपयोग आ अपूर्ण प्रतियोगिता जइसन पहलुअन के शामिल कइल गइल। एह विकास से मैक्रोइकोनॉमिक्स आ माइक्रोइकोनॉमिक्स के बीच के कई पुरान सैद्धांतिक असंगतियन के दूर करे के कोशिश भइल। मैक्रोइकोनॉमिक एनालिसिस में ओह कारकन के भी अध्ययन कइल जाला जे राष्ट्रीय आय के दीर्घकालिक स्तर आ [[आर्थिक विकास]] के प्रभावित करेलें। एहमें पूँजी संचय, तकनीकी प्रगति आ श्रम शक्ति के वृद्धि प्रमुख कारक मानल जालें।
=== बेरोजगारी ===
बेरोजगारी दर अर्थव्यवस्था में [[बेरोजगारी]] के स्तर के मापेला। ई लेबर फोर्स में शामिल ओह लोग के प्रतिशत बतावेला जे काम करे के इच्छुक आ सक्षम बाड़ें, बाकिर जिनका लगे रोजगार नइखे। लेबर फोर्स में खाली उहे लोग शामिल होलें जे सक्रिय रूप से नौकरी खोजत रहेलें। रिटायर लोग, पढ़ाई करत विद्यार्थी, भा ओह लोग जे रोजगार के अवसर ना मिले के कारण नौकरी खोजे के प्रयास छोड़ दिहले बाड़ें, लेबर फोर्स के हिस्सा ना मानल जालें। बेरोजगारी के आमतौर पर कई प्रकार में बाँटल जाला, आ हर प्रकार के पीछे अलग-अलग कारण होले।
क्लासिकल बेरोजगारी तब पैदा हो सके ला जब मजदूरी के दर एतना अधिक हो जाव कि नियोक्ता (रोजगार देवे वाला) अधिक श्रमिक रखे में असमर्थ भा अनिच्छुक हो जावें। एह स्थिति में रोजगार के अवसर सीमित हो जाला। एकरे अलावा फ्रिक्शनल बेरोजगारी ओह स्थिति के कहल जाला जब उपयुक्त नौकरी उपलब्ध त रहेला, बाकिर नौकरी खोजे, जानकारी प्राप्त करे आ नियुक्ति होखे में कुछ समय लागेला। एह बीच के अवधि में व्यक्ति बेरोजगार रहेला। ई बेरोजगारी आमतौर पर अस्थायी किसिम के होले आ श्रम बाजार के सामान्य कार्यप्रणाली के हिस्सा मानल जाले।
{{Clear}}
==संदर्भ==
{{Reflist}}
{{Economics}}
{{Authority control}}
[[श्रेणी:अर्थशास्त्र]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:एकैडमिक बिसय]]
{{बिज्ञान-आधार}}
8qds2s569alkmn7xtroplx9jbvjqiwl
गोरखनाथ
0
47678
797591
795898
2026-06-09T03:08:21Z
SM7
3953
अंग्रेजी विकिपीडिया से अनुबाद क के बिस्तार कइल गइल
797591
wikitext
text/x-wiki
{{Infobox Hindu leader
| name = गुरु गोरखनाथ
| image = Gorakshanath.jpg
| native_name =
| caption = लक्ष्मणगढ़ के मंदिर में गोरखनाथ के मूर्ती
| birth_date = ~ 11वीं सदी
| honors = ''महायोगी''
| founder = नाथ मठ आ मंदिर
| guru = [[मछेंदरनाथ]]
| known_for = हठ योग,<!-- {{sfn|Guy L. Beck|1995|pp=102-103}}{{sfn| Encyclopedia Britannica | 2007}} --> नाथ संप्रदाय, गोरखा, [[गोरखपुर]]
| literary_works =
}}
'''गुरु गोरखनाथ''' ({{Langx|sa|गोरक्षनाथ}}) [[हिंदू धर्म|हिंदू परंपरा]] में के जोगी लोग के [[नाथ पंथ]] के स्थापना करे वाला संत रहलें।<ref>George Weston Briggs (1938), ''Gorakhnath and the Kanphata Yogis'', 6th Edition (2009 Reprint), Motilal Banarsidass. {{ISBN|978-8120805644}}, p. 228</ref> इनके जीवन के बारे में बहुत जानकारी ना बा। कथा के मोताबिक ई [[मछेंदरनाथ]] के चेला रहलें। गोरखनाथ द्वारा स्थापित मत के माने वाला लोग जोगी, दर्शनी, भा कनफटवा कहाला आ ई लोग [[हिमालय]] के [[तराई]] के इलाका में [[पंजाब]] से ले के [[बिहार]] ले फइलल बा आ [[नेपाल|नैपालो]] में मजिगर संख्या में बा।
गोरखनाथ के हिंदू परंपरा में "महायोगी" मानल जाला। ऊ [[नवनाथ]] परंपरा के नौ गो संत लोगन में आवे लें। एह आध्यात्मिक परंपरा में हिंदू देवता ([[त्रिदेव]] में सामिल) [[शिव]] के पहिला गुरु मानल जाला। गोरख के जिनगी के बारे में जे कथा-कहानी चलन में बाड़ी उनहन में बतावल जाला कि ऊ समय के बंधन से बाहर रहलन आ अलग-अलग जुग में धरती पर प्रकट भइलन।
गोरखनाथ कवनो एक खास दार्शनिक सिद्धांत चाहे अंतिम सत्य पर जोर ना देलन। उनकर मानन रहे कि बिना पक्षपात के सत्य के खोज कइल मनुष्य के स्वाभाविक आ मूल्यवान लक्ष्य हवे। ऊ [[योग]], [[अध्यात्म|आध्यात्मिक अनुशासन]] आ अनुभवी गुरु के मार्गदर्शन के समाधि के आ आध्यात्मिक मुक्ति तक ले पहुँचे के सबसे जरूरी साधन मनलन।
उत्तर प्रदेश के [[गोरखपुर]] शहर इनहीं के नाँव पर बसल शहर बा जहाँ परसिद्ध [[गोरखनाथ मठ]] बाटे। नेपाल में एगो जिला [[गोरखा जिला]] इनहीं के नाँव पर हवे आ नेपाल आ भारत के तराई इलाका में रहे वाला [[गोरखा]] लोग के नाँव इनहीं के नाँव पर पड़ल हवे। नेपाल के राजशाही के दौरान इनका के मूल देवता मानल जाव। [[पशुपतिनाथ मंदिर]] में गोरखनाथ के अलगा से अस्थान दिहल गइल बाटे।
गुरु गोरख के प्रसिद्ध रचना सभ में ''गोरख गीता'', ''योग सिद्धांत पद्धति'', ''योगबीज'', आ ''योगचिंतामणि'' नियन कई ग्रंथ बाड़ें। गोरखनाथ के कबितई में रचल चीज सभ [[हिंदी साहित्य]] के अंग के रूप में ''[[गोरखबानी]]'' के नाँव से संकलित कइल गइल बाड़ी स।
== जिनगी ==
===इतिहासी मत ===
इतिहासकार लोग एह बात पर सहमत बा कि गोरखनाथ के जीवनकाल ईस्वी सन् के दुसरी सहस्राब्दी (मिलेनियम) के पहिला आधा हिस्सा में पड़ेला, हालाँकि ऊ ठीक कवन सदी में रहलें, एह पर मतभेद बा। पुरातात्त्विक आ साहित्यिक प्रमाणन के आधार पर कुछ विद्वान उनका के 11वीं–12वीं सदी के मानेलें, जबकि कुछ अउरी बिद्वान लोग उनका के 14वीं सदी में रखेलें। एबॉट के अनुसार, [[बाबा फरीद]] से जुड़ल दस्तावेज आ ज्ञानेश्वरी के पांडुलिपि गोरखनाथ के 13वीं सदी में स्थापित करेली। दुसरे ओर, [[जार्ज ग्रियर्सन|ग्रियर्सन]] गुजरात से मिलल प्रमाणन के आधार पर उनका के 14वीं सदी के मानेलें। गोरखनाथ के उल्लेख [[कबीर]] आ [[गुरु नानक]] के कविता सभ में भी मिलेला, जहाँ उनका के बहुत प्रभावशाली नेता आ विशाल अनुयायी समूह वाला संत के रूप में वर्णित कइल गइल बा।
ऐतिहासिक ग्रंथन से संकेत मिलेला कि गोरखनाथ शुरुआत में अइसन क्षेत्र में बौद्ध परंपरा से जुड़ल रहलें जहाँ शैव धर्म के प्रभाव मजबूत रहे। बाद में ऊ हिन्दू धर्म से जुड़ गइलें आ शिव आ योग के प्रमुख समर्थक बनलें। गोरखनाथ के जीवन आ विचार पर [[कुमारिल भट्ट]] आ [[आदि शंकराचार्य]] के प्रभाव देखल जाला। ऊ योग आ अद्वैत वेदांत के दृष्टिकोण से उपनिषद के व्याख्या के समर्थन करत रहलें। गोरखनाथ के मत में मध्यकालीन भारत में द्वैतवाद आ अद्वैतवाद के बीच के विवाद व्यावहारिक दृष्टि से बहुत उपयोगी ना रहे। उनका अनुसार, असली महत्त्व एह बात के बा कि योगी कवन मार्ग अपनावेला। ऊ मानत रहलें कि आध्यात्मिक अनुशासन, साधना आ योगाभ्यास के माध्यम से, चाहे कवनो मार्ग चुनल जाव, साधक आखिरकार व्यक्तिगत चेतना के पूर्ण रूप से प्रकाशित समाधि अवस्था तक पहुँच सकेला।
== इहो देखल जाय ==
* [[नाथ पंथ]]
* [[गोरखनाथ मठ]]
{{clear}}
==संदर्भ==
{{Reflist|35em}}
[[श्रेणी:गोरखपुर]]
[[श्रेणी:उत्तर प्रदेश के लोग]]
[[श्रेणी:हिंदू लोग]]
[[श्रेणी:हिंदू संत]]
[[श्रेणी:भारतीय हिंदू योगी]]
{{hinduism-stub}}
oz4vzd2jby5yo7f4t931az3iw6xslyy
797592
797591
2026-06-09T03:10:28Z
SM7
3953
अंग्रेजी विकिपीडिया से अनुबाद क के बिस्तार कइल गइल
797592
wikitext
text/x-wiki
{{Infobox Hindu leader
| name = गुरु गोरखनाथ
| image = Gorakshanath.jpg
| native_name =
| caption = लक्ष्मणगढ़ के मंदिर में गोरखनाथ के मूर्ती
| birth_date = ~ 11वीं सदी
| honors = ''महायोगी''
| founder = नाथ मठ आ मंदिर
| guru = [[मछेंदरनाथ]]
| known_for = हठ योग,<!-- {{sfn|Guy L. Beck|1995|pp=102-103}}{{sfn| Encyclopedia Britannica | 2007}} --> नाथ संप्रदाय, गोरखा, [[गोरखपुर]]
| literary_works =
}}
'''गुरु गोरखनाथ''' ({{Langx|sa|गोरक्षनाथ}}) [[हिंदू धर्म|हिंदू परंपरा]] में के जोगी लोग के [[नाथ पंथ]] के स्थापना करे वाला संत रहलें।<ref>George Weston Briggs (1938), ''Gorakhnath and the Kanphata Yogis'', 6th Edition (2009 Reprint), Motilal Banarsidass. {{ISBN|978-8120805644}}, p. 228</ref> इनके जीवन के बारे में बहुत जानकारी ना बा। कथा के मोताबिक ई [[मछेंदरनाथ]] के चेला रहलें। गोरखनाथ द्वारा स्थापित मत के माने वाला लोग जोगी, दर्शनी, भा कनफटवा कहाला आ ई लोग [[हिमालय]] के [[तराई]] के इलाका में [[पंजाब]] से ले के [[बिहार]] ले फइलल बा आ [[नेपाल|नैपालो]] में मजिगर संख्या में बा।
गोरखनाथ के हिंदू परंपरा में "महायोगी" मानल जाला। ऊ [[नवनाथ]] परंपरा के नौ गो संत लोगन में आवे लें। एह आध्यात्मिक परंपरा में हिंदू देवता ([[त्रिदेव]] में सामिल) [[शिव]] के पहिला गुरु मानल जाला। गोरख के जिनगी के बारे में जे कथा-कहानी चलन में बाड़ी उनहन में बतावल जाला कि ऊ समय के बंधन से बाहर रहलन आ अलग-अलग जुग में धरती पर प्रकट भइलन।
गोरखनाथ कवनो एक खास दार्शनिक सिद्धांत चाहे अंतिम सत्य पर जोर ना देलन। उनकर मानन रहे कि बिना पक्षपात के सत्य के खोज कइल मनुष्य के स्वाभाविक आ मूल्यवान लक्ष्य हवे। ऊ [[योग]], [[अध्यात्म|आध्यात्मिक अनुशासन]] आ अनुभवी गुरु के मार्गदर्शन के समाधि के आ आध्यात्मिक मुक्ति तक ले पहुँचे के सबसे जरूरी साधन मनलन।
उत्तर प्रदेश के [[गोरखपुर]] शहर इनहीं के नाँव पर बसल शहर बा जहाँ परसिद्ध [[गोरखनाथ मठ]] बाटे। नेपाल में एगो जिला [[गोरखा जिला]] इनहीं के नाँव पर हवे आ नेपाल आ भारत के तराई इलाका में रहे वाला [[गोरखा]] लोग के नाँव इनहीं के नाँव पर पड़ल हवे। नेपाल के राजशाही के दौरान इनका के मूल देवता मानल जाव। [[पशुपतिनाथ मंदिर]] में गोरखनाथ के अलगा से अस्थान दिहल गइल बाटे।
गुरु गोरख के प्रसिद्ध रचना सभ में ''गोरख गीता'', ''योग सिद्धांत पद्धति'', ''योगबीज'', आ ''योगचिंतामणि'' नियन कई ग्रंथ बाड़ें। गोरखनाथ के कबितई में रचल चीज सभ [[हिंदी साहित्य]] के अंग के रूप में ''[[गोरखबानी]]'' के नाँव से संकलित कइल गइल बाड़ी स।
== जिनगी ==
===इतिहासी मत ===
इतिहासकार लोग एह बात पर सहमत बा कि गोरखनाथ के जीवनकाल ईस्वी सन् के दुसरी सहस्राब्दी (मिलेनियम) के पहिला आधा हिस्सा में पड़ेला, हालाँकि ऊ ठीक कवन सदी में रहलें, एह पर मतभेद बा। पुरातात्त्विक आ साहित्यिक प्रमाणन के आधार पर कुछ विद्वान उनका के 11वीं–12वीं सदी के मानेलें, जबकि कुछ अउरी बिद्वान लोग उनका के 14वीं सदी में रखेलें। एबॉट के अनुसार, [[बाबा फरीद]] से जुड़ल दस्तावेज आ ज्ञानेश्वरी के पांडुलिपि गोरखनाथ के 13वीं सदी में स्थापित करेली। दुसरे ओर, [[जार्ज ग्रियर्सन|ग्रियर्सन]] गुजरात से मिलल प्रमाणन के आधार पर उनका के 14वीं सदी के मानेलें। गोरखनाथ के उल्लेख [[कबीर]] आ [[गुरु नानक]] के कविता सभ में भी मिलेला, जहाँ उनका के बहुत प्रभावशाली नेता आ विशाल अनुयायी समूह वाला संत के रूप में वर्णित कइल गइल बा।
ऐतिहासिक ग्रंथन से संकेत मिलेला कि गोरखनाथ शुरुआत में अइसन क्षेत्र में बौद्ध परंपरा से जुड़ल रहलें जहाँ शैव धर्म के प्रभाव मजबूत रहे। बाद में ऊ हिन्दू धर्म से जुड़ गइलें आ शिव आ योग के प्रमुख समर्थक बनलें। गोरखनाथ के जीवन आ विचार पर [[कुमारिल भट्ट]] आ [[आदि शंकराचार्य]] के प्रभाव देखल जाला। ऊ योग आ अद्वैत वेदांत के दृष्टिकोण से उपनिषद के व्याख्या के समर्थन करत रहलें। गोरखनाथ के मत में मध्यकालीन भारत में द्वैतवाद आ अद्वैतवाद के बीच के विवाद व्यावहारिक दृष्टि से बहुत उपयोगी ना रहे। उनका अनुसार, असली महत्त्व एह बात के बा कि योगी कवन मार्ग अपनावेला। ऊ मानत रहलें कि आध्यात्मिक अनुशासन, साधना आ योगाभ्यास के माध्यम से, चाहे कवनो मार्ग चुनल जाव, साधक आखिरकार व्यक्तिगत चेतना के पूर्ण रूप से प्रकाशित समाधि अवस्था तक पहुँच सकेला।
=== हागियोग्राफिक विवरण ===
गोरखनाथ से जुड़ल संत-चरित (हागियोग्राफी) साहित्य में उनका पृथ्वी पर कई बेर प्रकट होखे के वर्णन मिलेला। एह लोककथा आ धार्मिक विवरणन में उनका जन्म के निश्चित समय भा स्थान के उल्लेख नइखे मिलत, बल्कि उनका के अलौकिक आ मानवीय सीमा से परे व्यक्तित्व के रूप में प्रस्तुत कइल गइल बा। उत्तर भारत के हागियोग्राफिक परंपरा के अनुसार गोरखनाथ के उत्पत्ति उत्तर-पश्चिम भारत, खासकर पंजाब क्षेत्र से मानल जाला, जबकि कुछ विवरण में पेशावर के भी उल्लेख बा। एकरा विपरीत, बंगाल आ बिहार के कुछ हागियोग्राफी में उनका के भारत के पूर्वी क्षेत्र, विशेष रूप से असम, से संबंधित बतावल गइल बा।
उपलब्ध हागियोग्राफिक स्रोत गोरखनाथ के आध्यात्मिक गुरु-परंपरा के बारे में भी अलग-अलग विवरण देलें। सभे विवरण आदिनाथ आ मत्स्येन्द्रनाथ के गोरखनाथ से पहिले के गुरु के रूप में स्वीकार करेलें। हालाँकि, कुछ परंपरन में आदिनाथ से पहिले पाँच गुरु के उल्लेख मिलेला, जबकि दोसर विवरण में मत्स्येन्द्रनाथ आ गोरखनाथ के बीच छह गो गुरु के नाम बतावल गइल बा। वर्तमान नाथ परंपरा के अनुसार आदिनाथ, जिनका के आमतौर पर शिव के रूप मानल जाला, मत्स्येन्द्रनाथ के प्रत्यक्ष गुरु रहलें। मत्स्येन्द्रनाथ के बाद गोरखनाथ उनका प्रत्यक्ष शिष्य आ उत्तराधिकारी मानल जालें।
== इहो देखल जाय ==
* [[नाथ पंथ]]
* [[गोरखनाथ मठ]]
{{clear}}
==संदर्भ==
{{Reflist|35em}}
[[श्रेणी:गोरखपुर]]
[[श्रेणी:उत्तर प्रदेश के लोग]]
[[श्रेणी:हिंदू लोग]]
[[श्रेणी:हिंदू संत]]
[[श्रेणी:भारतीय हिंदू योगी]]
{{hinduism-stub}}
l0aus9j8mt3mowv77gfj7prltqjdbz9
797593
797592
2026-06-09T03:40:28Z
SM7
3953
संदर्भ जोड़ल/सुधारल गइल
797593
wikitext
text/x-wiki
{{Infobox Hindu leader
| name = गुरु गोरखनाथ
| image = Gorakshanath.jpg
| native_name =
| caption = लक्ष्मणगढ़ के मंदिर में गोरखनाथ के मूर्ती
| birth_date = ~ 11वीं सदी
| honors = ''महायोगी''
| founder = नाथ मठ आ मंदिर
| guru = [[मछेंदरनाथ]]
| known_for = हठ योग,<!-- {{sfn|Guy L. Beck|1995|pp=102-103}}{{sfn| Encyclopedia Britannica | 2007}} --> नाथ संप्रदाय, गोरखा, [[गोरखपुर]]
| literary_works =
}}
'''गुरु गोरखनाथ''' ({{Langx|sa|गोरक्षनाथ}}) [[हिंदू धर्म|हिंदू परंपरा]] में के जोगी लोग के [[नाथ पंथ]] के स्थापना करे वाला संत रहलें।<ref>George Weston Briggs (1938), ''Gorakhnath and the Kanphata Yogis'', 6th Edition (2009 Reprint), Motilal Banarsidass. {{ISBN|978-8120805644}}, p. 228</ref> इनके जीवन के बारे में बहुत जानकारी ना बा। कथा के मोताबिक ई [[मछेंदरनाथ]] के चेला रहलें। गोरखनाथ द्वारा स्थापित मत के माने वाला लोग जोगी, दर्शनी, भा कनफटवा कहाला आ ई लोग [[हिमालय]] के [[तराई]] के इलाका में [[पंजाब]] से ले के [[बिहार]] ले फइलल बा आ [[नेपाल|नैपालो]] में मजिगर संख्या में बा।
गोरखनाथ के हिंदू परंपरा में "महायोगी" मानल जाला। ऊ [[नवनाथ]] परंपरा के नौ गो संत लोगन में आवे लें। एह आध्यात्मिक परंपरा में हिंदू देवता ([[त्रिदेव]] में सामिल) [[शिव]] के पहिला गुरु मानल जाला। गोरख के जिनगी के बारे में जे कथा-कहानी चलन में बाड़ी उनहन में बतावल जाला कि ऊ समय के बंधन से बाहर रहलन आ अलग-अलग जुग में धरती पर प्रकट भइलन।
गोरखनाथ कवनो एक खास दार्शनिक सिद्धांत चाहे अंतिम सत्य पर जोर ना देलन। उनकर मानन रहे कि बिना पक्षपात के सत्य के खोज कइल मनुष्य के स्वाभाविक आ मूल्यवान लक्ष्य हवे। ऊ [[योग]], [[अध्यात्म|आध्यात्मिक अनुशासन]] आ अनुभवी गुरु के मार्गदर्शन के समाधि के आ आध्यात्मिक मुक्ति तक ले पहुँचे के सबसे जरूरी साधन मनलन।
उत्तर प्रदेश के [[गोरखपुर]] शहर इनहीं के नाँव पर बसल शहर बा जहाँ परसिद्ध [[गोरखनाथ मठ]] बाटे। नेपाल में एगो जिला [[गोरखा जिला]] इनहीं के नाँव पर हवे आ नेपाल आ भारत के तराई इलाका में रहे वाला [[गोरखा]] लोग के नाँव इनहीं के नाँव पर पड़ल हवे। नेपाल के राजशाही के दौरान इनका के मूल देवता मानल जाव। [[पशुपतिनाथ मंदिर]] में गोरखनाथ के अलगा से अस्थान दिहल गइल बाटे।
गुरु गोरख के प्रसिद्ध रचना सभ में ''गोरख गीता'', ''योग सिद्धांत पद्धति'', ''योगबीज'', आ ''योगचिंतामणि'' नियन कई ग्रंथ बाड़ें। गोरखनाथ के कबितई में रचल चीज सभ [[हिंदी साहित्य]] के अंग के रूप में ''[[गोरखबानी]]'' के नाँव से संकलित कइल गइल बाड़ी स।
== जिनगी ==
===इतिहासी मत ===
इतिहासकार लोग एह बात पर सहमत बा कि गोरखनाथ के जीवनकाल ईस्वी सन् के दुसरी सहस्राब्दी (मिलेनियम) के पहिला आधा हिस्सा में पड़ेला, हालाँकि ऊ ठीक कवन सदी में रहलें, एह पर मतभेद बा। पुरातात्त्विक आ साहित्यिक प्रमाणन के आधार पर कुछ विद्वान उनका के 11वीं–12वीं सदी के मानेलें,{{sfn|Briggs|1938|p=249}} जबकि कुछ अउरी बिद्वान लोग उनका के 14वीं सदी में रखेलें।{{sfn|Briggs|1938|pp=228–230}} एबॉट के अनुसार, [[बाबा फरीद]] से जुड़ल दस्तावेज आ ज्ञानेश्वरी के पांडुलिपि गोरखनाथ के 13वीं सदी में स्थापित करेली।{{sfn|Briggs|1938|pp=230, 242–243}} दुसरे ओर, [[जार्ज ग्रियर्सन|ग्रियर्सन]] गुजरात से मिलल प्रमाणन के आधार पर उनका के 14वीं सदी के मानेलें।{{sfn|Briggs|1938|pp=230, 242–243}} गोरखनाथ के उल्लेख [[कबीर]] आ [[गुरु नानक]] के कविता सभ में भी मिलेला, जहाँ उनका के बहुत प्रभावशाली नेता आ विशाल अनुयायी समूह वाला संत के रूप में वर्णित कइल गइल बा।{{sfn|Briggs|1938|pp=236–242}}
ऐतिहासिक ग्रंथन से संकेत मिलेला कि गोरखनाथ शुरुआत में अइसन क्षेत्र में [[बौद्ध धर्म|बौद्ध परंपरा]] से जुड़ल रहलें जहाँ [[शैव मत|शैव धर्म]] के प्रभाव मजबूत रहे। बाद में ऊ [[हिंदू धर्म]] से जुड़ गइलें आ [[शिव]] आ [[योग]] के प्रमुख समर्थक बनलें।{{sfn|Briggs|1938|pp=229, 233–235}} गोरखनाथ के जीवन आ विचार पर [[कुमारिल भट्ट]] आ [[आदि शंकराचार्य]] के प्रभाव देखल जाला जेकरा चलते ऊ योग आ [[अद्वैत वेदांत]] के दृष्टिकोण से [[उपनिषद]] सभ के व्याख्या के समर्थन कइलें।{{sfn|Akshaya Kumar Banerjea|1983|pp=xli, 303–307}} गोरखनाथ के मत में मध्यकालीन भारत में [[द्वैतवाद]] आ [[अद्वैतवाद]] के बीच के विवाद व्यावहारिक दृष्टि से बहुत उपयोगी ना रहे। उनका अनुसार, असली महत्त्व एह बात के बा कि योगी कवन मार्ग अपनावेला। ऊ मानत रहलें कि आध्यात्मिक अनुशासन, साधना आ योगाभ्यास के माध्यम से, चाहे कवनो मार्ग चुनल जाव, साधक आखिरकार व्यक्तिगत चेतना के पूर्ण रूप से प्रकाशित समाधि अवस्था तक पहुँच सकेला।{{sfn|Akshaya Kumar Banerjea|1983|pp=xli, 307–312}}
=== हागियोग्राफिक विवरण ===
गोरखनाथ से जुड़ल संत-चरित (हागियोग्राफी) साहित्य में उनका पृथ्वी पर कई बेर प्रकट होखे के वर्णन मिलेला। एह लोककथा आ धार्मिक विवरणन में उनका जन्म के निश्चित समय भा स्थान के उल्लेख नइखे मिलत, बल्कि उनका के अलौकिक आ मानवीय सीमा से परे व्यक्तित्व के रूप में प्रस्तुत कइल गइल बा। उत्तर भारत के हागियोग्राफिक परंपरा के अनुसार गोरखनाथ के उत्पत्ति उत्तर-पश्चिम भारत, खासकर पंजाब क्षेत्र से मानल जाला, जबकि कुछ विवरण में पेशावर के भी उल्लेख बा। एकरा विपरीत, बंगाल आ बिहार के कुछ हागियोग्राफी में उनका के भारत के पूर्वी क्षेत्र, विशेष रूप से असम, से संबंधित बतावल गइल बा।
उपलब्ध हागियोग्राफिक स्रोत गोरखनाथ के आध्यात्मिक गुरु-परंपरा के बारे में भी अलग-अलग विवरण देलें। सभे विवरण आदिनाथ आ मत्स्येन्द्रनाथ के गोरखनाथ से पहिले के गुरु के रूप में स्वीकार करेलें। हालाँकि, कुछ परंपरन में आदिनाथ से पहिले पाँच गुरु के उल्लेख मिलेला, जबकि दोसर विवरण में मत्स्येन्द्रनाथ आ गोरखनाथ के बीच छह गो गुरु के नाम बतावल गइल बा। वर्तमान नाथ परंपरा के अनुसार आदिनाथ, जिनका के आमतौर पर शिव के रूप मानल जाला, मत्स्येन्द्रनाथ के प्रत्यक्ष गुरु रहलें। मत्स्येन्द्रनाथ के बाद गोरखनाथ उनका प्रत्यक्ष शिष्य आ उत्तराधिकारी मानल जालें।
== इहो देखल जाय ==
* [[नाथ पंथ]]
* [[गोरखनाथ मठ]]
{{clear}}
==संदर्भ==
{{Reflist|35em}}
[[श्रेणी:गोरखपुर]]
[[श्रेणी:उत्तर प्रदेश के लोग]]
[[श्रेणी:हिंदू लोग]]
[[श्रेणी:हिंदू संत]]
[[श्रेणी:भारतीय हिंदू योगी]]
{{hinduism-stub}}
oewqpwd7i9lxqsqmcmg6lt1re7tee77
797596
797593
2026-06-09T06:44:07Z
SM7
3953
भाषा/ब्याकरण संबंधी सुधार कइल गइल
797596
wikitext
text/x-wiki
{{Infobox Hindu leader
| name = गुरु गोरखनाथ
| image = Gorakshanath.jpg
| native_name =
| caption = लक्ष्मणगढ़ के मंदिर में गोरखनाथ के मूर्ती
| birth_date = ~ 11वीं सदी
| honors = ''महायोगी''
| founder = नाथ मठ आ मंदिर
| guru = [[मछेंदरनाथ]]
| known_for = हठ योग,<!-- {{sfn|Guy L. Beck|1995|pp=102-103}}{{sfn| Encyclopedia Britannica | 2007}} --> नाथ संप्रदाय, गोरखा, [[गोरखपुर]]
| literary_works =
}}
'''गुरु गोरखनाथ''' ({{Langx|sa|गोरक्षनाथ}}) [[हिंदू धर्म|हिंदू परंपरा]] में के जोगी लोग के [[नाथ पंथ]] के स्थापना करे वाला संत रहलें।<ref>George Weston Briggs (1938), ''Gorakhnath and the Kanphata Yogis'', 6th Edition (2009 Reprint), Motilal Banarsidass. {{ISBN|978-8120805644}}, p. 228</ref> इनके जीवन के बारे में बहुत जानकारी ना बा। कथा के मोताबिक ई [[मछेंदरनाथ]] के चेला रहलें। गोरखनाथ द्वारा स्थापित मत के माने वाला लोग जोगी, दर्शनी, भा कनफटवा कहाला आ ई लोग [[हिमालय]] के [[तराई]] के इलाका में [[पंजाब]] से ले के [[बिहार]] ले फइलल बा आ [[नेपाल|नैपालो]] में मजिगर संख्या में बा।
गोरखनाथ के हिंदू परंपरा में "महायोगी" मानल जाला। ऊ [[नवनाथ]] परंपरा के नौ गो संत लोगन में आवे लें। एह आध्यात्मिक परंपरा में हिंदू देवता ([[त्रिदेव]] में सामिल) [[शिव]] के पहिला गुरु मानल जाला। गोरख के जिनगी के बारे में जे कथा-कहानी चलन में बाड़ी उनहन में बतावल जाला कि ऊ समय के बंधन से बाहर रहलन आ अलग-अलग जुग में धरती पर प्रकट भइलन।
गोरखनाथ कवनो एक खास दार्शनिक सिद्धांत चाहे अंतिम सत्य पर जोर ना देलन। उनकर मानन रहे कि बिना पक्षपात के सत्य के खोज कइल मनुष्य के स्वाभाविक आ मूल्यवान लक्ष्य हवे। ऊ [[योग]], [[अध्यात्म|आध्यात्मिक अनुशासन]] आ अनुभवी गुरु के मार्गदर्शन के समाधि के आ आध्यात्मिक मुक्ति तक ले पहुँचे के सबसे जरूरी साधन मनलन।
उत्तर प्रदेश के [[गोरखपुर]] शहर इनहीं के नाँव पर बसल शहर बा जहाँ परसिद्ध [[गोरखनाथ मठ]] बाटे। नेपाल में एगो जिला [[गोरखा जिला]] इनहीं के नाँव पर हवे आ नेपाल आ भारत के तराई इलाका में रहे वाला [[गोरखा]] लोग के नाँव इनहीं के नाँव पर पड़ल हवे। नेपाल के राजशाही के दौरान इनका के मूल देवता मानल जाव। [[पशुपतिनाथ मंदिर]] में गोरखनाथ के अलगा से अस्थान दिहल गइल बाटे।
गुरु गोरख के प्रसिद्ध रचना सभ में ''गोरख गीता'', ''योग सिद्धांत पद्धति'', ''योगबीज'', आ ''योगचिंतामणि'' नियन कई ग्रंथ बाड़ें। गोरखनाथ के कबितई में रचल चीज सभ [[हिंदी साहित्य]] के अंग के रूप में ''[[गोरखबानी]]'' के नाँव से संकलित कइल गइल बाड़ी स।
== जिनगी ==
===इतिहासी मत ===
इतिहासकार लोग एह बात पर सहमत बा कि गोरखनाथ के जीवनकाल ईस्वी सन् के दुसरी सहस्राब्दी (मिलेनियम) के पहिला आधा हिस्सा में पड़ेला, हालाँकि ऊ ठीक कवन सदी में रहलें, एह पर मतभेद बा। पुरातात्त्विक आ साहित्यिक प्रमाणन के आधार पर कुछ विद्वान उनका के 11वीं–12वीं सदी के मानेलें,{{sfn|Briggs|1938|p=249}} जबकि कुछ अउरी बिद्वान लोग उनका के 14वीं सदी में रखेलें।{{sfn|Briggs|1938|pp=228–230}} एबॉट के अनुसार, [[बाबा फरीद]] से जुड़ल दस्तावेज आ ज्ञानेश्वरी के पांडुलिपि गोरखनाथ के 13वीं सदी में स्थापित करेली।{{sfn|Briggs|1938|pp=230, 242–243}} दुसरे ओर, [[जार्ज ग्रियर्सन|ग्रियर्सन]] गुजरात से मिलल प्रमाणन के आधार पर उनका के 14वीं सदी के मानेलें।{{sfn|Briggs|1938|pp=230, 242–243}} गोरखनाथ के उल्लेख [[कबीर]] आ [[गुरु नानक]] के कविता सभ में भी मिलेला, जहाँ उनका के बहुत प्रभावशाली नेता आ विशाल अनुयायी समूह वाला संत के रूप में वर्णित कइल गइल बा।{{sfn|Briggs|1938|pp=236–242}}
ऐतिहासिक ग्रंथन से संकेत मिलेला कि गोरखनाथ शुरुआत में अइसन क्षेत्र में [[बौद्ध धर्म|बौद्ध परंपरा]] से जुड़ल रहलें जहाँ [[शैव मत|शैव धर्म]] के प्रभाव मजबूत रहे। बाद में ऊ [[हिंदू धर्म]] से जुड़ गइलें आ [[शिव]] आ [[योग]] के प्रमुख समर्थक बनलें।{{sfn|Briggs|1938|pp=229, 233–235}} गोरखनाथ के जीवन आ विचार पर [[कुमारिल भट्ट]] आ [[आदि शंकराचार्य]] के प्रभाव देखल जाला जेकरा चलते ऊ योग आ [[अद्वैत वेदांत]] के दृष्टिकोण से [[उपनिषद]] सभ के व्याख्या के समर्थन कइलें।{{sfn|Akshaya Kumar Banerjea|1983|pp=xli, 303–307}} गोरखनाथ के मत में मध्यकालीन भारत में [[द्वैतवाद]] आ [[अद्वैतवाद]] के बीच के विवाद व्यावहारिक दृष्टि से बहुत उपयोगी ना रहे। उनका अनुसार, असली महत्त्व एह बात के बा कि योगी कवन मार्ग अपनावेला। ऊ मानत रहलें कि आध्यात्मिक अनुशासन, साधना आ योगाभ्यास के माध्यम से, चाहे कवनो मार्ग चुनल जाव, साधक आखिरकार व्यक्तिगत चेतना के पूर्ण रूप से प्रकाशित समाधि अवस्था तक पहुँच सकेला।{{sfn|Akshaya Kumar Banerjea|1983|pp=xli, 307–312}}
=== हैगियोग्राफिक विवरण ===
गोरखनाथ से जुड़ल संत-चरित ([[हैगियोग्राफी]]) साहित्य में उनकरा पृथ्वी पर कई बेर प्रकट होखे के वर्णन मिलेला। एह लोककथा आ धार्मिक विवरणन में उनका जनम के निश्चित समय भा जगह (लोकेशन) के उल्लेख ना मिले ला, बलुक उनका के अलौकिक आ मानवीय सीमा से परे व्यक्तित्व के रूप में प्रस्तुत कइल जाला। उत्तर भारत के हैगियोग्राफिक परंपरा के अनुसार गोरखनाथ के उत्पत्ति उत्तर-पश्चिम भारत, खासकर पंजाब क्षेत्र से मानल जाला, जबकि कुछ विवरण में पेशावर के भी उल्लेख मिले ला। एकरा विपरीत, बंगाल आ बिहार के कुछ हैगियोग्राफी में उनका के भारत के पूर्वी क्षेत्र, विशेष रूप से आसाम, से संबंधित बतावल गइल बा।
उपलब्ध हैगियोग्राफिक स्रोत गोरखनाथ के आध्यात्मिक गुरु-परंपरा के बारे में भी अलग-अलग विवरण देलें। सभे बिबरन [[आदिनाथ]] आ [[मछेंदरनाथ]] के गोरखनाथ से पहिले के गुरु के रूप में स्वीकार करे लें। हालाँकि, कुछ परंपरन में आदिनाथ से पहिले पाँच गो अउरी गुरु लोगन के जिकिर मिलेला, जबकि दुसरे बिबरन में मछेंदरनाथ आ गोरखनाथ के बीचा में छह गो गुरु लोग के नाँव बतावल गइल बा। वर्तमान नाथ परंपरा के अनुसार आदिनाथ, जिनका के आमतौर पर [[शिव|शिवजी]] के रूप मानल जाला, मछेंदरनाथ के के प्रत्यक्ष गुरु रहलें। मछेंदर के बाद गोरखनाथ उनका प्रत्यक्ष शिष्य आ उत्तराधिकारी मानल जालें।
== इहो देखल जाय ==
* [[नाथ पंथ]]
* [[गोरखनाथ मठ]]
{{clear}}
==संदर्भ==
{{Reflist|35em}}
[[श्रेणी:गोरखपुर]]
[[श्रेणी:उत्तर प्रदेश के लोग]]
[[श्रेणी:हिंदू लोग]]
[[श्रेणी:हिंदू संत]]
[[श्रेणी:भारतीय हिंदू योगी]]
{{hinduism-stub}}
91h8st7529sn7zmmwft16xcqq3oswa7
सूरदास
0
51494
797565
683951
2026-06-09T00:05:07Z
SM7
3953
बिस्तार कइल गइल
797565
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image =
| alt =
| caption = एकतारा बजावत आ गावत सूरदास, कल्पित चित्र
| birth_date = अनिश्चित (1478-1483)
| birth_place = मान्यता [[ब्रज]], [[उत्तर प्रदेश]], [[भारत]]
| death_date = अनिश्चित (1561-1584)
| death_place = ब्रज, उत्तर प्रदेश, भारत
| known_for = Influencing the [[Bhakti movement]], [[Sant Mat]], Hymns in the [[Guru Granth Sahib]]
| literary_works = ''सूरसागर'', ''सूरसारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father =
| mother =
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें। सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, वल्लभाचार्य के दीक्षित चेला रहलें। उनकर जीवन कथा चौरासी वैष्णवन की वार्ता जइसन ग्रंथन में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीलान के चित्रण मिलेला।
{{clear}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
{{poet-stub}}
nnnrxeqzi8vqlb8uzeonqivy5j5tdsm
797566
797565
2026-06-09T00:11:41Z
SM7
3953
सुधार कइल गइल
797566
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image = Surdas, detail of a painting of Surdas with a devoted Brahmin, Kishangarh style, Rajasthan, circa 18th century (cropped).jpg
| alt = Surdas
| caption = सूरदास के एगो पेंटिंग, किशनगढ़ स्टाइल, राजस्थान, लगभग 18वीं सदी
| birth_date = {{circa|1483}}
| birth_place = सीही गाँव, [[फरीदाबाद]], [[दिल्ली सल्तनत]]
| death_date = {{circa|1563}}
| death_place = [[ब्रज]] परसौली, [[मुगल राज]]
| known_for = [[भक्ति आंदोलन]] के अगुआ, [[संत मत]],
| literary_works = ''[[सूरसागर]]'', ''सूर सारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father = रामदास सारस्वत<ref name="knowledgeocean.in">{{Cite web|url=https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|archive-url=https://web.archive.org/web/20200921003713/https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|url-status=usurped|archive-date=21 September 2020|title = सूरदास का जीवन परिचय - Biography of Surdas in Hindi Jivan Parichay| date=16 September 2020 }}</ref>
| mother = जमुनादास<ref name="knowledgeocean.in"/>
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें। सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, वल्लभाचार्य के दीक्षित चेला रहलें। उनकर जीवन कथा चौरासी वैष्णवन की वार्ता जइसन ग्रंथन में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीला सभ के चित्रण मिलेला।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
{{poet-stub}}
1pmm1kol48d2qxd58l6o5u2x242yejc
797567
797566
2026-06-09T00:42:39Z
SM7
3953
संदर्भ जोड़ल/सुधारल गइल
797567
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image = Surdas, detail of a painting of Surdas with a devoted Brahmin, Kishangarh style, Rajasthan, circa 18th century (cropped).jpg
| alt = Surdas
| caption = सूरदास के एगो पेंटिंग, किशनगढ़ स्टाइल, राजस्थान, लगभग 18वीं सदी
| birth_date = {{circa|1483}}
| birth_place = सीही गाँव, [[फरीदाबाद]], [[दिल्ली सल्तनत]]
| death_date = {{circa|1563}}
| death_place = [[ब्रज]] परसौली, [[मुगल राज]]
| known_for = [[भक्ति आंदोलन]] के अगुआ, [[संत मत]],
| literary_works = ''[[सूरसागर]]'', ''सूर सारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father = रामदास सारस्वत<ref name="knowledgeocean.in">{{Cite web|url=https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|archive-url=https://web.archive.org/web/20200921003713/https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|url-status=usurped|archive-date=21 September 2020|title = सूरदास का जीवन परिचय - Biography of Surdas in Hindi Jivan Parichay| date=16 September 2020 }}</ref>
| mother = जमुनादास<ref name="knowledgeocean.in"/>
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें।<ref name=Klaustermaier>{{cite book|author=Klaus K. Klostermaier|title=A Survey of Hinduism: Third Edition| url=https://books.google.com/books?id=E_6-JbUiHB4C&pg=PA215| date=2007-07-05| publisher=SUNY Press| isbn=978-0-7914-7082-4|page=215}}</ref> सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।<ref name=":0">{{Cite web |date=2018-06-17 |title=Surdas Biography - Surdas Poems - Life History in English |url=https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |access-date=2022-04-26 |website=India the Destiny |language=en-US |archive-date=26 June 2022 |archive-url=https://web.archive.org/web/20220626144617/https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |url-status=dead }}</ref>
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, वल्लभाचार्य के दीक्षित चेला रहलें। उनकर जीवन कथा चौरासी वैष्णवन की वार्ता जइसन ग्रंथन में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीला सभ के चित्रण मिलेला।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
{{poet-stub}}
dfjzixxzqdwmneahev60w0cklbdbexd
797568
797567
2026-06-09T00:55:19Z
SM7
3953
अंग्रेजी विकिपीडिया से अनुबाद क के बिस्तार कइल गइल
797568
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image = Surdas, detail of a painting of Surdas with a devoted Brahmin, Kishangarh style, Rajasthan, circa 18th century (cropped).jpg
| alt = Surdas
| caption = सूरदास के एगो पेंटिंग, किशनगढ़ स्टाइल, राजस्थान, लगभग 18वीं सदी
| birth_date = {{circa|1483}}
| birth_place = सीही गाँव, [[फरीदाबाद]], [[दिल्ली सल्तनत]]
| death_date = {{circa|1563}}
| death_place = [[ब्रज]] परसौली, [[मुगल राज]]
| known_for = [[भक्ति आंदोलन]] के अगुआ, [[संत मत]],
| literary_works = ''[[सूरसागर]]'', ''सूर सारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father = रामदास सारस्वत<ref name="knowledgeocean.in">{{Cite web|url=https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|archive-url=https://web.archive.org/web/20200921003713/https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|url-status=usurped|archive-date=21 September 2020|title = सूरदास का जीवन परिचय - Biography of Surdas in Hindi Jivan Parichay| date=16 September 2020 }}</ref>
| mother = जमुनादास<ref name="knowledgeocean.in"/>
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें।<ref name=Klaustermaier>{{cite book|author=Klaus K. Klostermaier|title=A Survey of Hinduism: Third Edition| url=https://books.google.com/books?id=E_6-JbUiHB4C&pg=PA215| date=2007-07-05| publisher=SUNY Press| isbn=978-0-7914-7082-4|page=215}}</ref> सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।<ref name=":0">{{Cite web |date=2018-06-17 |title=Surdas Biography - Surdas Poems - Life History in English |url=https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |access-date=2022-04-26 |website=India the Destiny |language=en-US |archive-date=26 June 2022 |archive-url=https://web.archive.org/web/20220626144617/https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |url-status=dead }}</ref>
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, [[वल्लभाचार्य]] के दीक्षित चेला रहलें। उनकर जीवन कथा ''[[चौरासी वैष्णवन की वार्ता]]'' ग्रंथ में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।<ref name=":02">{{Cite book |last=Hawley |first=John Stratton |title=Brill's Encyclopedia of Hinduism Online |publisher=Brill |year=2018 |editor-last=Jacobsen |editor-first=Knut A. |chapter=Sūrdās |editor-last2=Basu |editor-first2=Helene |editor-last3=Malinar |editor-first3=Angelika |editor-last4=Narayanan |editor-first4=Vasudha}}</ref>
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीला सभ के चित्रण मिलेला।
== जिनगी आ कबितई ==
सूरदास के जन्म के बारे में अलग-अलग मत मिलेला। Encyclopaedia of Indian Literature के अनुसार उनकर जन्म 1258 ईस्वी में उत्तर प्रदेश के एगो ब्राह्मण परिवार में भइल रहे। जबकि Encyclopaedia Britannica में उनकर जीवनकाल परंपरागत रूप से 1483 से 1563 ईस्वी बतावल गइल बा। कुछ स्रोत उनकर संबंध सारस्वत ब्राह्मण, जाट भा ढाढ़ी समुदाय से जोड़ेलें।
सूरदास के नाम के अर्थ "सूर्य के सेवक" होला। ब्रज भाषा के काव्य परंपरा में उनका के सर्वोच्च कवियन में गिनल जाला। ब्रज भाषा के संबंध व्रज क्षेत्र से बा, जहाँ हिन्दू परंपरा के अनुसार कृष्ण आपन बचपन बितवले रहलें।
भक्त कवियन के जीवनचरित लिखे वाला नाभादास अपना ग्रंथ भक्तमाल में सूरदास के काव्य प्रतिभा के खूब प्रशंसा कइले बाड़ें। खास तौर पर ऊ कृष्ण के बाल लीला आ दिव्य क्रीड़ा के चित्रण में सूरदास के असाधारण कौशल के सराहना करेलें। सूरदास राम आ सीता पर भी कुछ पद लिखले रहलें, बाकिर उनकर अधिकांश रचना कृष्ण के जीवन, चरित्र आ लीलन पर केंद्रित बा।
=== कबितई ===
सूरदास के कविता के रचना मुख्य रूप से [[ब्रजभाषा]] में बाड़ी स। ओह जमाना में साहित्य के प्रमुख भाषा [[संस्कृत]] आ [[फ़ारसी]] मानल जालीं, जबकि ब्रज भाषा के आम लोग के बोली समझल जाय। सूरदास के रचना सभ ब्रजभाषा के साहित्यिक प्रतिष्ठा दियावे में महत्त्वपूर्ण भूमिका निभवलीं स। उनकर काव्य के कारण ब्रजभाषा के दर्जा एगो साधारण बोली से बढ़ के साहित्य के समृद्ध भाषा के रूप में स्थापित भइल।
सूरदास के नाम से जुड़ल कविता सभ के एकट्ठा रूप से ''[[सूरसागर]]'' कहल जाला, जेकर अर्थ "सूर के सागर" होला। एह नाम के कारण ई बा कि उनका नाम से बहुत बड़हन संख्या में पद आ कविता सभ जुड़ल बाड़ी स। परंपरागत रूप से सूरसागर के बारह भाग में बाँटल जाला, जवन संरचना में [[श्रीमद्भागवत|भागवत पुराण]] से मिलत-जुलत बा। जइसे भागवत पुराण में कृष्ण के जीवन आ लीला सभ के बर्णन मिले ला , ओइसहीं सूरसागर के अधिकांश पद कृष्ण के जीवन, बाल लीला आ भक्ति पर केंद्रित बाड़ें। एह ग्रंथ में मिलल बहुत पद छह से दस गो तुकांत लाइन वाला गीतात्मक रचना के रूप में लिखल गइल हवें।
कृष्ण के अलावा सूरदास के काव्य में राम आ सीता, विष्णु, शिव, आ हिन्दू परंपरा के पात्र जइसे गजेंद्र आ राजा बलि के भी वर्णन मिलेला। एकरे साथे उनका रचना में कवि के निजी आध्यात्मिक संघर्ष, भक्ति भावना आ ईश्वर से आत्मिक जुड़ाव के अभिव्यक्ति भी देखे के मिलेला।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
{{poet-stub}}
tb3fqtpkxoj5k6yxvqj29b1nfq1d3bc
797569
797568
2026-06-09T01:24:39Z
SM7
3953
अंग्रेजी विकिपीडिया से अनुबाद क के बिस्तार कइल गइल
797569
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image = Surdas, detail of a painting of Surdas with a devoted Brahmin, Kishangarh style, Rajasthan, circa 18th century (cropped).jpg
| alt = Surdas
| caption = सूरदास के एगो पेंटिंग, किशनगढ़ स्टाइल, राजस्थान, लगभग 18वीं सदी
| birth_date = {{circa|1483}}
| birth_place = सीही गाँव, [[फरीदाबाद]], [[दिल्ली सल्तनत]]
| death_date = {{circa|1563}}
| death_place = [[ब्रज]] परसौली, [[मुगल राज]]
| known_for = [[भक्ति आंदोलन]] के अगुआ, [[संत मत]],
| literary_works = ''[[सूरसागर]]'', ''सूर सारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father = रामदास सारस्वत<ref name="knowledgeocean.in">{{Cite web|url=https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|archive-url=https://web.archive.org/web/20200921003713/https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|url-status=usurped|archive-date=21 September 2020|title = सूरदास का जीवन परिचय - Biography of Surdas in Hindi Jivan Parichay| date=16 September 2020 }}</ref>
| mother = जमुनादास<ref name="knowledgeocean.in"/>
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें।<ref name=Klaustermaier>{{cite book|author=Klaus K. Klostermaier|title=A Survey of Hinduism: Third Edition| url=https://books.google.com/books?id=E_6-JbUiHB4C&pg=PA215| date=2007-07-05| publisher=SUNY Press| isbn=978-0-7914-7082-4|page=215}}</ref> सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।<ref name=":0">{{Cite web |date=2018-06-17 |title=Surdas Biography - Surdas Poems - Life History in English |url=https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |access-date=2022-04-26 |website=India the Destiny |language=en-US |archive-date=26 June 2022 |archive-url=https://web.archive.org/web/20220626144617/https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |url-status=dead }}</ref>
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, [[वल्लभाचार्य]] के दीक्षित चेला रहलें। उनकर जीवन कथा ''[[चौरासी वैष्णवन की वार्ता]]'' ग्रंथ में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।<ref name=":02">{{Cite book |last=Hawley |first=John Stratton |title=Brill's Encyclopedia of Hinduism Online |publisher=Brill |year=2018 |editor-last=Jacobsen |editor-first=Knut A. |chapter=Sūrdās |editor-last2=Basu |editor-first2=Helene |editor-last3=Malinar |editor-first3=Angelika |editor-last4=Narayanan |editor-first4=Vasudha}}</ref>
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीला सभ के चित्रण मिलेला।
== जिनगी आ कबितई ==
सूरदास के जन्म के बारे में अलग-अलग मत मिलेला। Encyclopaedia of Indian Literature के अनुसार उनकर जन्म 1258 ईस्वी में उत्तर प्रदेश के एगो ब्राह्मण परिवार में भइल रहे। जबकि Encyclopaedia Britannica में उनकर जीवनकाल परंपरागत रूप से 1483 से 1563 ईस्वी बतावल गइल बा। कुछ स्रोत उनकर संबंध सारस्वत ब्राह्मण, जाट भा ढाढ़ी समुदाय से जोड़ेलें।
सूरदास के नाम के अर्थ "सूर्य के सेवक" होला। ब्रज भाषा के काव्य परंपरा में उनका के सर्वोच्च कवियन में गिनल जाला। ब्रज भाषा के संबंध व्रज क्षेत्र से बा, जहाँ हिन्दू परंपरा के अनुसार कृष्ण आपन बचपन बितवले रहलें।
भक्त कवियन के जीवनचरित लिखे वाला नाभादास अपना ग्रंथ भक्तमाल में सूरदास के काव्य प्रतिभा के खूब प्रशंसा कइले बाड़ें। खास तौर पर ऊ कृष्ण के बाल लीला आ दिव्य क्रीड़ा के चित्रण में सूरदास के असाधारण कौशल के सराहना करेलें। सूरदास राम आ सीता पर भी कुछ पद लिखले रहलें, बाकिर उनकर अधिकांश रचना कृष्ण के जीवन, चरित्र आ लीलन पर केंद्रित बा।
=== कबितई ===
सूरदास के कविता के रचना मुख्य रूप से [[ब्रजभाषा]] में बाड़ी स। ओह जमाना में साहित्य के प्रमुख भाषा [[संस्कृत]] आ [[फ़ारसी]] मानल जालीं, जबकि ब्रज भाषा के आम लोग के बोली समझल जाय। सूरदास के रचना सभ ब्रजभाषा के साहित्यिक प्रतिष्ठा दियावे में महत्त्वपूर्ण भूमिका निभवलीं स। उनकर काव्य के कारण ब्रजभाषा के दर्जा एगो साधारण बोली से बढ़ के साहित्य के समृद्ध भाषा के रूप में स्थापित भइल।
सूरदास के नाम से जुड़ल कविता सभ के एकट्ठा रूप से ''[[सूरसागर]]'' कहल जाला, जेकर अर्थ "सूर के सागर" होला। एह नाम के कारण ई बा कि उनका नाम से बहुत बड़हन संख्या में पद आ कविता सभ जुड़ल बाड़ी स। परंपरागत रूप से सूरसागर के बारह भाग में बाँटल जाला, जवन संरचना में [[श्रीमद्भागवत|भागवत पुराण]] से मिलत-जुलत बा। जइसे भागवत पुराण में कृष्ण के जीवन आ लीला सभ के बर्णन मिले ला , ओइसहीं सूरसागर के अधिकांश पद कृष्ण के जीवन, बाल लीला आ भक्ति पर केंद्रित बाड़ें। एह ग्रंथ में मिलल बहुत पद छह से दस गो तुकांत लाइन वाला गीतात्मक रचना के रूप में लिखल गइल हवें।
कृष्ण के अलावा सूरदास के काव्य में राम आ सीता, विष्णु, शिव, आ हिन्दू परंपरा के पात्र जइसे गजेंद्र आ राजा बलि के भी वर्णन मिलेला। एकरे साथे उनका रचना में कवि के निजी आध्यात्मिक संघर्ष, भक्ति भावना आ ईश्वर से आत्मिक जुड़ाव के अभिव्यक्ति भी देखे के मिलेला।
== दर्शन ==
[[वल्लभाचार्य]] के आठ प्रमुख शिष्यन के सामूहिक रूप से ''[[अष्टछाप]]'' कहल जाला। "अष्टछाप" के अर्थ "आठ गो मुहर" भा "आठ गो हस्ताक्षर" होला। एह नाम के संबंध ओह काव्य परंपरा से बा, जहाँ कवि अपना रचना के अंत में आपन छाप (काव्य-हस्ताक्षर) जोड़त रहलें। अष्टछाप के कवियन में सूरदास के सबसे प्रमुख आ श्रेष्ठ कवि मानल जाला।
== पॉपुलर कल्चर में ==
सूरदास के जिनगी पर आधारित कई ठे फिलिम बन चुकल बाड़ी स। एहमें ''सूरदास'' (1939), ''भक्त सूरदास'' (1942), ''संत सूरदास'' (1975) आ ''चिंतामणि सूरदास'' (1988) प्रमुख बाड़ी स।
आँख से आन्हर एगो दूसर भक्त कवि बिल्वमंगल (जिनका के कुछ परंपरन में सूरदास से जोड़ल जाला) आ चिंतामणि से जुड़ल लोककथा के भारतीय सिनेमा में कई बेर रूपांतरित कइल गइल बा। एह कथा पर आधारित प्रमुख फिल्मन में "बिल्वमंगल" भा "भगत सूरदास" (1919), "बिल्वमंगल" (1932), "चिंतामणि" (1933), "चिंतामणि" (1937), "भक्त बिल्वमंगल" (1948), "बिल्वमंगल" (1954), "भक्त बिल्वमंगल" (1954), "चिंतामणि" (1956), "चिंतामणि" (1957), "चिलम्बोली" (1963), "बिल्वमंगल" (1976) आ "विल्वमंगल की प्रतिज्ञा" (1996) शामिल बाड़ी स। एह फिल्मन में बिल्वमंगल के आध्यात्मिक परिवर्तन, भक्ति आ चिंतामणि से जुड़ल कथा के अलग-अलग रूप में प्रस्तुत कइल गइल बा।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
{{poet-stub}}
fo361f31yn93h6klghb6kc7uwyc3bnl
797587
797569
2026-06-09T01:48:22Z
SM7
3953
/* पॉपुलर कल्चर में */ सुधार कइल गइल
797587
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image = Surdas, detail of a painting of Surdas with a devoted Brahmin, Kishangarh style, Rajasthan, circa 18th century (cropped).jpg
| alt = Surdas
| caption = सूरदास के एगो पेंटिंग, किशनगढ़ स्टाइल, राजस्थान, लगभग 18वीं सदी
| birth_date = {{circa|1483}}
| birth_place = सीही गाँव, [[फरीदाबाद]], [[दिल्ली सल्तनत]]
| death_date = {{circa|1563}}
| death_place = [[ब्रज]] परसौली, [[मुगल राज]]
| known_for = [[भक्ति आंदोलन]] के अगुआ, [[संत मत]],
| literary_works = ''[[सूरसागर]]'', ''सूर सारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father = रामदास सारस्वत<ref name="knowledgeocean.in">{{Cite web|url=https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|archive-url=https://web.archive.org/web/20200921003713/https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|url-status=usurped|archive-date=21 September 2020|title = सूरदास का जीवन परिचय - Biography of Surdas in Hindi Jivan Parichay| date=16 September 2020 }}</ref>
| mother = जमुनादास<ref name="knowledgeocean.in"/>
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें।<ref name=Klaustermaier>{{cite book|author=Klaus K. Klostermaier|title=A Survey of Hinduism: Third Edition| url=https://books.google.com/books?id=E_6-JbUiHB4C&pg=PA215| date=2007-07-05| publisher=SUNY Press| isbn=978-0-7914-7082-4|page=215}}</ref> सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।<ref name=":0">{{Cite web |date=2018-06-17 |title=Surdas Biography - Surdas Poems - Life History in English |url=https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |access-date=2022-04-26 |website=India the Destiny |language=en-US |archive-date=26 June 2022 |archive-url=https://web.archive.org/web/20220626144617/https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |url-status=dead }}</ref>
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, [[वल्लभाचार्य]] के दीक्षित चेला रहलें। उनकर जीवन कथा ''[[चौरासी वैष्णवन की वार्ता]]'' ग्रंथ में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।<ref name=":02">{{Cite book |last=Hawley |first=John Stratton |title=Brill's Encyclopedia of Hinduism Online |publisher=Brill |year=2018 |editor-last=Jacobsen |editor-first=Knut A. |chapter=Sūrdās |editor-last2=Basu |editor-first2=Helene |editor-last3=Malinar |editor-first3=Angelika |editor-last4=Narayanan |editor-first4=Vasudha}}</ref>
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीला सभ के चित्रण मिलेला।
== जिनगी आ कबितई ==
सूरदास के जन्म के बारे में अलग-अलग मत मिलेला। Encyclopaedia of Indian Literature के अनुसार उनकर जन्म 1258 ईस्वी में उत्तर प्रदेश के एगो ब्राह्मण परिवार में भइल रहे। जबकि Encyclopaedia Britannica में उनकर जीवनकाल परंपरागत रूप से 1483 से 1563 ईस्वी बतावल गइल बा। कुछ स्रोत उनकर संबंध सारस्वत ब्राह्मण, जाट भा ढाढ़ी समुदाय से जोड़ेलें।
सूरदास के नाम के अर्थ "सूर्य के सेवक" होला। ब्रज भाषा के काव्य परंपरा में उनका के सर्वोच्च कवियन में गिनल जाला। ब्रज भाषा के संबंध व्रज क्षेत्र से बा, जहाँ हिन्दू परंपरा के अनुसार कृष्ण आपन बचपन बितवले रहलें।
भक्त कवियन के जीवनचरित लिखे वाला नाभादास अपना ग्रंथ भक्तमाल में सूरदास के काव्य प्रतिभा के खूब प्रशंसा कइले बाड़ें। खास तौर पर ऊ कृष्ण के बाल लीला आ दिव्य क्रीड़ा के चित्रण में सूरदास के असाधारण कौशल के सराहना करेलें। सूरदास राम आ सीता पर भी कुछ पद लिखले रहलें, बाकिर उनकर अधिकांश रचना कृष्ण के जीवन, चरित्र आ लीलन पर केंद्रित बा।
=== कबितई ===
सूरदास के कविता के रचना मुख्य रूप से [[ब्रजभाषा]] में बाड़ी स। ओह जमाना में साहित्य के प्रमुख भाषा [[संस्कृत]] आ [[फ़ारसी]] मानल जालीं, जबकि ब्रज भाषा के आम लोग के बोली समझल जाय। सूरदास के रचना सभ ब्रजभाषा के साहित्यिक प्रतिष्ठा दियावे में महत्त्वपूर्ण भूमिका निभवलीं स। उनकर काव्य के कारण ब्रजभाषा के दर्जा एगो साधारण बोली से बढ़ के साहित्य के समृद्ध भाषा के रूप में स्थापित भइल।
सूरदास के नाम से जुड़ल कविता सभ के एकट्ठा रूप से ''[[सूरसागर]]'' कहल जाला, जेकर अर्थ "सूर के सागर" होला। एह नाम के कारण ई बा कि उनका नाम से बहुत बड़हन संख्या में पद आ कविता सभ जुड़ल बाड़ी स। परंपरागत रूप से सूरसागर के बारह भाग में बाँटल जाला, जवन संरचना में [[श्रीमद्भागवत|भागवत पुराण]] से मिलत-जुलत बा। जइसे भागवत पुराण में कृष्ण के जीवन आ लीला सभ के बर्णन मिले ला , ओइसहीं सूरसागर के अधिकांश पद कृष्ण के जीवन, बाल लीला आ भक्ति पर केंद्रित बाड़ें। एह ग्रंथ में मिलल बहुत पद छह से दस गो तुकांत लाइन वाला गीतात्मक रचना के रूप में लिखल गइल हवें।
कृष्ण के अलावा सूरदास के काव्य में राम आ सीता, विष्णु, शिव, आ हिन्दू परंपरा के पात्र जइसे गजेंद्र आ राजा बलि के भी वर्णन मिलेला। एकरे साथे उनका रचना में कवि के निजी आध्यात्मिक संघर्ष, भक्ति भावना आ ईश्वर से आत्मिक जुड़ाव के अभिव्यक्ति भी देखे के मिलेला।
== दर्शन ==
[[वल्लभाचार्य]] के आठ प्रमुख शिष्यन के सामूहिक रूप से ''[[अष्टछाप]]'' कहल जाला। "अष्टछाप" के अर्थ "आठ गो मुहर" भा "आठ गो हस्ताक्षर" होला। एह नाम के संबंध ओह काव्य परंपरा से बा, जहाँ कवि अपना रचना के अंत में आपन छाप (काव्य-हस्ताक्षर) जोड़त रहलें। अष्टछाप के कवियन में सूरदास के सबसे प्रमुख आ श्रेष्ठ कवि मानल जाला।
== पॉपुलर कल्चर में ==
सूरदास के जिनगी पर आधारित कई ठे फिलिम बन चुकल बाड़ी स। एहमें ''सूरदास'' (1939), ''भक्त सूरदास'' (1942), ''संत सूरदास'' (1975) आ ''चिंतामणि सूरदास'' (1988) प्रमुख बाड़ी स।
आँख से आन्हर एगो दूसर भक्त कवि बिल्वमंगल (जिनका के कुछ परंपरन में सूरदास से जोड़ल जाला) आ चिंतामणि के कथा के भारतीय सिनेमा में कई बेर रूपांतरित कइल गइल बा। एह कथा पर आधारित फिल्मन में "बिल्वमंगल" भा "भगत सूरदास" (1919), जेकर निर्देशन रुस्तमजी धोतीवाला कइले रहलें; "बिल्वमंगल" (1932); "चिंतामणि" (1933), जेकर निर्देशन कल्लाकुरी सदाशिव राव कइले रहलें; "चिंतामणि" (1937), जेकर निर्देशन वाई. वी. राव कइले रहलें; "भक्त बिल्वमंगल" (1948), जेकर निर्देशन शांति कुमार कइले रहलें; "बिल्वमंगल" (1954), जेकर निर्देशन डी. एन. माधोक कइले रहलें; "भक्त बिल्वमंगल" (1954), जेकर निर्देशन पिनाकी भूषण मुखर्जी कइले रहलें; "चिंतामणि" (1956), जेकर निर्देशन पी. एस. रामकृष्ण राव कइले रहलें; "चिंतामणि" (1957), जेकर निर्देशन एम. एन. बसवराजैया कइले रहलें; "चिलम्बोली" (1963), जेकर निर्देशन जी. के. रामू कइले रहलें; "बिल्वमंगल" (1976), जेकर निर्देशन गोबिंद राय कइले रहलें; आ "विल्वमंगल की प्रतिज्ञा" (1996), जेकर निर्देशन संजय विरमानी कइले रहलें, शामिल बाड़ी स।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
{{poet-stub}}
pbohj60wx9jaaxksa6h0q466shetzaz
797588
797587
2026-06-09T01:48:41Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:संत मत]] जोड़ल गइल
797588
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image = Surdas, detail of a painting of Surdas with a devoted Brahmin, Kishangarh style, Rajasthan, circa 18th century (cropped).jpg
| alt = Surdas
| caption = सूरदास के एगो पेंटिंग, किशनगढ़ स्टाइल, राजस्थान, लगभग 18वीं सदी
| birth_date = {{circa|1483}}
| birth_place = सीही गाँव, [[फरीदाबाद]], [[दिल्ली सल्तनत]]
| death_date = {{circa|1563}}
| death_place = [[ब्रज]] परसौली, [[मुगल राज]]
| known_for = [[भक्ति आंदोलन]] के अगुआ, [[संत मत]],
| literary_works = ''[[सूरसागर]]'', ''सूर सारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father = रामदास सारस्वत<ref name="knowledgeocean.in">{{Cite web|url=https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|archive-url=https://web.archive.org/web/20200921003713/https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|url-status=usurped|archive-date=21 September 2020|title = सूरदास का जीवन परिचय - Biography of Surdas in Hindi Jivan Parichay| date=16 September 2020 }}</ref>
| mother = जमुनादास<ref name="knowledgeocean.in"/>
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें।<ref name=Klaustermaier>{{cite book|author=Klaus K. Klostermaier|title=A Survey of Hinduism: Third Edition| url=https://books.google.com/books?id=E_6-JbUiHB4C&pg=PA215| date=2007-07-05| publisher=SUNY Press| isbn=978-0-7914-7082-4|page=215}}</ref> सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।<ref name=":0">{{Cite web |date=2018-06-17 |title=Surdas Biography - Surdas Poems - Life History in English |url=https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |access-date=2022-04-26 |website=India the Destiny |language=en-US |archive-date=26 June 2022 |archive-url=https://web.archive.org/web/20220626144617/https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |url-status=dead }}</ref>
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, [[वल्लभाचार्य]] के दीक्षित चेला रहलें। उनकर जीवन कथा ''[[चौरासी वैष्णवन की वार्ता]]'' ग्रंथ में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।<ref name=":02">{{Cite book |last=Hawley |first=John Stratton |title=Brill's Encyclopedia of Hinduism Online |publisher=Brill |year=2018 |editor-last=Jacobsen |editor-first=Knut A. |chapter=Sūrdās |editor-last2=Basu |editor-first2=Helene |editor-last3=Malinar |editor-first3=Angelika |editor-last4=Narayanan |editor-first4=Vasudha}}</ref>
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीला सभ के चित्रण मिलेला।
== जिनगी आ कबितई ==
सूरदास के जन्म के बारे में अलग-अलग मत मिलेला। Encyclopaedia of Indian Literature के अनुसार उनकर जन्म 1258 ईस्वी में उत्तर प्रदेश के एगो ब्राह्मण परिवार में भइल रहे। जबकि Encyclopaedia Britannica में उनकर जीवनकाल परंपरागत रूप से 1483 से 1563 ईस्वी बतावल गइल बा। कुछ स्रोत उनकर संबंध सारस्वत ब्राह्मण, जाट भा ढाढ़ी समुदाय से जोड़ेलें।
सूरदास के नाम के अर्थ "सूर्य के सेवक" होला। ब्रज भाषा के काव्य परंपरा में उनका के सर्वोच्च कवियन में गिनल जाला। ब्रज भाषा के संबंध व्रज क्षेत्र से बा, जहाँ हिन्दू परंपरा के अनुसार कृष्ण आपन बचपन बितवले रहलें।
भक्त कवियन के जीवनचरित लिखे वाला नाभादास अपना ग्रंथ भक्तमाल में सूरदास के काव्य प्रतिभा के खूब प्रशंसा कइले बाड़ें। खास तौर पर ऊ कृष्ण के बाल लीला आ दिव्य क्रीड़ा के चित्रण में सूरदास के असाधारण कौशल के सराहना करेलें। सूरदास राम आ सीता पर भी कुछ पद लिखले रहलें, बाकिर उनकर अधिकांश रचना कृष्ण के जीवन, चरित्र आ लीलन पर केंद्रित बा।
=== कबितई ===
सूरदास के कविता के रचना मुख्य रूप से [[ब्रजभाषा]] में बाड़ी स। ओह जमाना में साहित्य के प्रमुख भाषा [[संस्कृत]] आ [[फ़ारसी]] मानल जालीं, जबकि ब्रज भाषा के आम लोग के बोली समझल जाय। सूरदास के रचना सभ ब्रजभाषा के साहित्यिक प्रतिष्ठा दियावे में महत्त्वपूर्ण भूमिका निभवलीं स। उनकर काव्य के कारण ब्रजभाषा के दर्जा एगो साधारण बोली से बढ़ के साहित्य के समृद्ध भाषा के रूप में स्थापित भइल।
सूरदास के नाम से जुड़ल कविता सभ के एकट्ठा रूप से ''[[सूरसागर]]'' कहल जाला, जेकर अर्थ "सूर के सागर" होला। एह नाम के कारण ई बा कि उनका नाम से बहुत बड़हन संख्या में पद आ कविता सभ जुड़ल बाड़ी स। परंपरागत रूप से सूरसागर के बारह भाग में बाँटल जाला, जवन संरचना में [[श्रीमद्भागवत|भागवत पुराण]] से मिलत-जुलत बा। जइसे भागवत पुराण में कृष्ण के जीवन आ लीला सभ के बर्णन मिले ला , ओइसहीं सूरसागर के अधिकांश पद कृष्ण के जीवन, बाल लीला आ भक्ति पर केंद्रित बाड़ें। एह ग्रंथ में मिलल बहुत पद छह से दस गो तुकांत लाइन वाला गीतात्मक रचना के रूप में लिखल गइल हवें।
कृष्ण के अलावा सूरदास के काव्य में राम आ सीता, विष्णु, शिव, आ हिन्दू परंपरा के पात्र जइसे गजेंद्र आ राजा बलि के भी वर्णन मिलेला। एकरे साथे उनका रचना में कवि के निजी आध्यात्मिक संघर्ष, भक्ति भावना आ ईश्वर से आत्मिक जुड़ाव के अभिव्यक्ति भी देखे के मिलेला।
== दर्शन ==
[[वल्लभाचार्य]] के आठ प्रमुख शिष्यन के सामूहिक रूप से ''[[अष्टछाप]]'' कहल जाला। "अष्टछाप" के अर्थ "आठ गो मुहर" भा "आठ गो हस्ताक्षर" होला। एह नाम के संबंध ओह काव्य परंपरा से बा, जहाँ कवि अपना रचना के अंत में आपन छाप (काव्य-हस्ताक्षर) जोड़त रहलें। अष्टछाप के कवियन में सूरदास के सबसे प्रमुख आ श्रेष्ठ कवि मानल जाला।
== पॉपुलर कल्चर में ==
सूरदास के जिनगी पर आधारित कई ठे फिलिम बन चुकल बाड़ी स। एहमें ''सूरदास'' (1939), ''भक्त सूरदास'' (1942), ''संत सूरदास'' (1975) आ ''चिंतामणि सूरदास'' (1988) प्रमुख बाड़ी स।
आँख से आन्हर एगो दूसर भक्त कवि बिल्वमंगल (जिनका के कुछ परंपरन में सूरदास से जोड़ल जाला) आ चिंतामणि के कथा के भारतीय सिनेमा में कई बेर रूपांतरित कइल गइल बा। एह कथा पर आधारित फिल्मन में "बिल्वमंगल" भा "भगत सूरदास" (1919), जेकर निर्देशन रुस्तमजी धोतीवाला कइले रहलें; "बिल्वमंगल" (1932); "चिंतामणि" (1933), जेकर निर्देशन कल्लाकुरी सदाशिव राव कइले रहलें; "चिंतामणि" (1937), जेकर निर्देशन वाई. वी. राव कइले रहलें; "भक्त बिल्वमंगल" (1948), जेकर निर्देशन शांति कुमार कइले रहलें; "बिल्वमंगल" (1954), जेकर निर्देशन डी. एन. माधोक कइले रहलें; "भक्त बिल्वमंगल" (1954), जेकर निर्देशन पिनाकी भूषण मुखर्जी कइले रहलें; "चिंतामणि" (1956), जेकर निर्देशन पी. एस. रामकृष्ण राव कइले रहलें; "चिंतामणि" (1957), जेकर निर्देशन एम. एन. बसवराजैया कइले रहलें; "चिलम्बोली" (1963), जेकर निर्देशन जी. के. रामू कइले रहलें; "बिल्वमंगल" (1976), जेकर निर्देशन गोबिंद राय कइले रहलें; आ "विल्वमंगल की प्रतिज्ञा" (1996), जेकर निर्देशन संजय विरमानी कइले रहलें, शामिल बाड़ी स।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
[[श्रेणी:संत मत]]
{{poet-stub}}
ikt0awtq23z547ado7b3tdmz04v21aq
797589
797588
2026-06-09T01:49:00Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:हिंदू संत]] जोड़ल गइल
797589
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image = Surdas, detail of a painting of Surdas with a devoted Brahmin, Kishangarh style, Rajasthan, circa 18th century (cropped).jpg
| alt = Surdas
| caption = सूरदास के एगो पेंटिंग, किशनगढ़ स्टाइल, राजस्थान, लगभग 18वीं सदी
| birth_date = {{circa|1483}}
| birth_place = सीही गाँव, [[फरीदाबाद]], [[दिल्ली सल्तनत]]
| death_date = {{circa|1563}}
| death_place = [[ब्रज]] परसौली, [[मुगल राज]]
| known_for = [[भक्ति आंदोलन]] के अगुआ, [[संत मत]],
| literary_works = ''[[सूरसागर]]'', ''सूर सारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father = रामदास सारस्वत<ref name="knowledgeocean.in">{{Cite web|url=https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|archive-url=https://web.archive.org/web/20200921003713/https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|url-status=usurped|archive-date=21 September 2020|title = सूरदास का जीवन परिचय - Biography of Surdas in Hindi Jivan Parichay| date=16 September 2020 }}</ref>
| mother = जमुनादास<ref name="knowledgeocean.in"/>
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें।<ref name=Klaustermaier>{{cite book|author=Klaus K. Klostermaier|title=A Survey of Hinduism: Third Edition| url=https://books.google.com/books?id=E_6-JbUiHB4C&pg=PA215| date=2007-07-05| publisher=SUNY Press| isbn=978-0-7914-7082-4|page=215}}</ref> सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।<ref name=":0">{{Cite web |date=2018-06-17 |title=Surdas Biography - Surdas Poems - Life History in English |url=https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |access-date=2022-04-26 |website=India the Destiny |language=en-US |archive-date=26 June 2022 |archive-url=https://web.archive.org/web/20220626144617/https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |url-status=dead }}</ref>
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, [[वल्लभाचार्य]] के दीक्षित चेला रहलें। उनकर जीवन कथा ''[[चौरासी वैष्णवन की वार्ता]]'' ग्रंथ में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।<ref name=":02">{{Cite book |last=Hawley |first=John Stratton |title=Brill's Encyclopedia of Hinduism Online |publisher=Brill |year=2018 |editor-last=Jacobsen |editor-first=Knut A. |chapter=Sūrdās |editor-last2=Basu |editor-first2=Helene |editor-last3=Malinar |editor-first3=Angelika |editor-last4=Narayanan |editor-first4=Vasudha}}</ref>
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीला सभ के चित्रण मिलेला।
== जिनगी आ कबितई ==
सूरदास के जन्म के बारे में अलग-अलग मत मिलेला। Encyclopaedia of Indian Literature के अनुसार उनकर जन्म 1258 ईस्वी में उत्तर प्रदेश के एगो ब्राह्मण परिवार में भइल रहे। जबकि Encyclopaedia Britannica में उनकर जीवनकाल परंपरागत रूप से 1483 से 1563 ईस्वी बतावल गइल बा। कुछ स्रोत उनकर संबंध सारस्वत ब्राह्मण, जाट भा ढाढ़ी समुदाय से जोड़ेलें।
सूरदास के नाम के अर्थ "सूर्य के सेवक" होला। ब्रज भाषा के काव्य परंपरा में उनका के सर्वोच्च कवियन में गिनल जाला। ब्रज भाषा के संबंध व्रज क्षेत्र से बा, जहाँ हिन्दू परंपरा के अनुसार कृष्ण आपन बचपन बितवले रहलें।
भक्त कवियन के जीवनचरित लिखे वाला नाभादास अपना ग्रंथ भक्तमाल में सूरदास के काव्य प्रतिभा के खूब प्रशंसा कइले बाड़ें। खास तौर पर ऊ कृष्ण के बाल लीला आ दिव्य क्रीड़ा के चित्रण में सूरदास के असाधारण कौशल के सराहना करेलें। सूरदास राम आ सीता पर भी कुछ पद लिखले रहलें, बाकिर उनकर अधिकांश रचना कृष्ण के जीवन, चरित्र आ लीलन पर केंद्रित बा।
=== कबितई ===
सूरदास के कविता के रचना मुख्य रूप से [[ब्रजभाषा]] में बाड़ी स। ओह जमाना में साहित्य के प्रमुख भाषा [[संस्कृत]] आ [[फ़ारसी]] मानल जालीं, जबकि ब्रज भाषा के आम लोग के बोली समझल जाय। सूरदास के रचना सभ ब्रजभाषा के साहित्यिक प्रतिष्ठा दियावे में महत्त्वपूर्ण भूमिका निभवलीं स। उनकर काव्य के कारण ब्रजभाषा के दर्जा एगो साधारण बोली से बढ़ के साहित्य के समृद्ध भाषा के रूप में स्थापित भइल।
सूरदास के नाम से जुड़ल कविता सभ के एकट्ठा रूप से ''[[सूरसागर]]'' कहल जाला, जेकर अर्थ "सूर के सागर" होला। एह नाम के कारण ई बा कि उनका नाम से बहुत बड़हन संख्या में पद आ कविता सभ जुड़ल बाड़ी स। परंपरागत रूप से सूरसागर के बारह भाग में बाँटल जाला, जवन संरचना में [[श्रीमद्भागवत|भागवत पुराण]] से मिलत-जुलत बा। जइसे भागवत पुराण में कृष्ण के जीवन आ लीला सभ के बर्णन मिले ला , ओइसहीं सूरसागर के अधिकांश पद कृष्ण के जीवन, बाल लीला आ भक्ति पर केंद्रित बाड़ें। एह ग्रंथ में मिलल बहुत पद छह से दस गो तुकांत लाइन वाला गीतात्मक रचना के रूप में लिखल गइल हवें।
कृष्ण के अलावा सूरदास के काव्य में राम आ सीता, विष्णु, शिव, आ हिन्दू परंपरा के पात्र जइसे गजेंद्र आ राजा बलि के भी वर्णन मिलेला। एकरे साथे उनका रचना में कवि के निजी आध्यात्मिक संघर्ष, भक्ति भावना आ ईश्वर से आत्मिक जुड़ाव के अभिव्यक्ति भी देखे के मिलेला।
== दर्शन ==
[[वल्लभाचार्य]] के आठ प्रमुख शिष्यन के सामूहिक रूप से ''[[अष्टछाप]]'' कहल जाला। "अष्टछाप" के अर्थ "आठ गो मुहर" भा "आठ गो हस्ताक्षर" होला। एह नाम के संबंध ओह काव्य परंपरा से बा, जहाँ कवि अपना रचना के अंत में आपन छाप (काव्य-हस्ताक्षर) जोड़त रहलें। अष्टछाप के कवियन में सूरदास के सबसे प्रमुख आ श्रेष्ठ कवि मानल जाला।
== पॉपुलर कल्चर में ==
सूरदास के जिनगी पर आधारित कई ठे फिलिम बन चुकल बाड़ी स। एहमें ''सूरदास'' (1939), ''भक्त सूरदास'' (1942), ''संत सूरदास'' (1975) आ ''चिंतामणि सूरदास'' (1988) प्रमुख बाड़ी स।
आँख से आन्हर एगो दूसर भक्त कवि बिल्वमंगल (जिनका के कुछ परंपरन में सूरदास से जोड़ल जाला) आ चिंतामणि के कथा के भारतीय सिनेमा में कई बेर रूपांतरित कइल गइल बा। एह कथा पर आधारित फिल्मन में "बिल्वमंगल" भा "भगत सूरदास" (1919), जेकर निर्देशन रुस्तमजी धोतीवाला कइले रहलें; "बिल्वमंगल" (1932); "चिंतामणि" (1933), जेकर निर्देशन कल्लाकुरी सदाशिव राव कइले रहलें; "चिंतामणि" (1937), जेकर निर्देशन वाई. वी. राव कइले रहलें; "भक्त बिल्वमंगल" (1948), जेकर निर्देशन शांति कुमार कइले रहलें; "बिल्वमंगल" (1954), जेकर निर्देशन डी. एन. माधोक कइले रहलें; "भक्त बिल्वमंगल" (1954), जेकर निर्देशन पिनाकी भूषण मुखर्जी कइले रहलें; "चिंतामणि" (1956), जेकर निर्देशन पी. एस. रामकृष्ण राव कइले रहलें; "चिंतामणि" (1957), जेकर निर्देशन एम. एन. बसवराजैया कइले रहलें; "चिलम्बोली" (1963), जेकर निर्देशन जी. के. रामू कइले रहलें; "बिल्वमंगल" (1976), जेकर निर्देशन गोबिंद राय कइले रहलें; आ "विल्वमंगल की प्रतिज्ञा" (1996), जेकर निर्देशन संजय विरमानी कइले रहलें, शामिल बाड़ी स।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
[[श्रेणी:संत मत]]
[[श्रेणी:हिंदू संत]]
{{poet-stub}}
te938j7cti1ygjc89bw6kvb7i92rw2a
797590
797589
2026-06-09T01:49:18Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:वैष्णव संप्रदाय]] जोड़ल गइल
797590
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[हिंदू धर्म]]
| name = सूरदास
| image = Surdas, detail of a painting of Surdas with a devoted Brahmin, Kishangarh style, Rajasthan, circa 18th century (cropped).jpg
| alt = Surdas
| caption = सूरदास के एगो पेंटिंग, किशनगढ़ स्टाइल, राजस्थान, लगभग 18वीं सदी
| birth_date = {{circa|1483}}
| birth_place = सीही गाँव, [[फरीदाबाद]], [[दिल्ली सल्तनत]]
| death_date = {{circa|1563}}
| death_place = [[ब्रज]] परसौली, [[मुगल राज]]
| known_for = [[भक्ति आंदोलन]] के अगुआ, [[संत मत]],
| literary_works = ''[[सूरसागर]]'', ''सूर सारावली'', ''साहित्य लहरी''
| philosophy = [[भक्ति]]
| father = रामदास सारस्वत<ref name="knowledgeocean.in">{{Cite web|url=https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|archive-url=https://web.archive.org/web/20200921003713/https://knowledgeocean.in/biography-of-surdas-in-hindi-jivan-parichay-%E0%A4%B8%E0%A5%82%E0%A4%B0%E0%A4%A6%E0%A4%BE%E0%A4%B8-%E0%A4%95%E0%A4%BE-%E0%A4%9C%E0%A5%80%E0%A4%B5%E0%A4%A8-%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%9A%E0%A4%AF/3290/|url-status=usurped|archive-date=21 September 2020|title = सूरदास का जीवन परिचय - Biography of Surdas in Hindi Jivan Parichay| date=16 September 2020 }}</ref>
| mother = जमुनादास<ref name="knowledgeocean.in"/>
}}
'''सूरदास''' 16वीं सदी के प्रसिद्ध [[भक्त]] [[कवि]] आ गायक रहलें।<ref name=Klaustermaier>{{cite book|author=Klaus K. Klostermaier|title=A Survey of Hinduism: Third Edition| url=https://books.google.com/books?id=E_6-JbUiHB4C&pg=PA215| date=2007-07-05| publisher=SUNY Press| isbn=978-0-7914-7082-4|page=215}}</ref> सूर नाँव पड़े के कारन उनुकर आँख से आन्हर भइल रहे। ऊ खास तौर पर [[कृष्ण]] के भक्ति में लिखल अपना पद आ [[भजन]] सभ खातिर जानल जालें। उनकर अधिकतर भक्ति गीत [[ब्रजभाषा]] में रचल गइल हवें, जबकि कुछ रचना मध्यकालीन हिंदी के अउरी दूसर बोली, जइसे [[अवधी भाषा|अवधी]], में भी मिले लीं।<ref name=":0">{{Cite web |date=2018-06-17 |title=Surdas Biography - Surdas Poems - Life History in English |url=https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |access-date=2022-04-26 |website=India the Destiny |language=en-US |archive-date=26 June 2022 |archive-url=https://web.archive.org/web/20220626144617/https://indiathedestiny.com/icons/poets-writers/surdas-biography/ |url-status=dead }}</ref>
सूरदास के जिनगी के कथा के वर्णन अक्सर पुष्टिमार्ग (वल्लभ संप्रदाय) के परंपरा के आधार पर कइल जाला। पुष्टिमार्ग के अनुसार सूरदास, [[वल्लभाचार्य]] के दीक्षित चेला रहलें। उनकर जीवन कथा ''[[चौरासी वैष्णवन की वार्ता]]'' ग्रंथ में मिलेला। सूरदास के पद, [[अष्टछाप]] के अउरी बाकी कवियन के रचना सभ के साथे, पुष्टिमार्ग के भजन-कीर्तन परंपरा के प्रमुख हिस्सा मानल जालें। हालाँकि, आधुनिक बिद्वान लोग सूरदास आ वल्लभाचार्य के बीच एह संबंध के ऐतिहासिक रूप से प्रमाणित ना मानेलें।<ref name=":02">{{Cite book |last=Hawley |first=John Stratton |title=Brill's Encyclopedia of Hinduism Online |publisher=Brill |year=2018 |editor-last=Jacobsen |editor-first=Knut A. |chapter=Sūrdās |editor-last2=Basu |editor-first2=Helene |editor-last3=Malinar |editor-first3=Angelika |editor-last4=Narayanan |editor-first4=Vasudha}}</ref>
''[[सूरसागर]]'' के परंपरागत रूप से सूरदास के रचल सभसे प्रमुख ग्रंथ मानल जाला। हालाँकि, विद्वान लोग के मान्यता ई बा कि एह ग्रंथ में शामिल कई पद बाद के कवियन द्वारा सूरदास के नाम से लिखल गइल हो सके लें। वर्तमान रूप में सूरसागर मुख्य रूप से गोकुल आ ब्रज के बालक कृष्ण के मनोहर लीला सभ के वर्णन करेला। एह पदन में अक्सर गोपी लोग के दृष्टिकोण से कृष्ण के बाल रूप आ उनकर प्रेममय लीला सभ के चित्रण मिलेला।
== जिनगी आ कबितई ==
सूरदास के जन्म के बारे में अलग-अलग मत मिलेला। Encyclopaedia of Indian Literature के अनुसार उनकर जन्म 1258 ईस्वी में उत्तर प्रदेश के एगो ब्राह्मण परिवार में भइल रहे। जबकि Encyclopaedia Britannica में उनकर जीवनकाल परंपरागत रूप से 1483 से 1563 ईस्वी बतावल गइल बा। कुछ स्रोत उनकर संबंध सारस्वत ब्राह्मण, जाट भा ढाढ़ी समुदाय से जोड़ेलें।
सूरदास के नाम के अर्थ "सूर्य के सेवक" होला। ब्रज भाषा के काव्य परंपरा में उनका के सर्वोच्च कवियन में गिनल जाला। ब्रज भाषा के संबंध व्रज क्षेत्र से बा, जहाँ हिन्दू परंपरा के अनुसार कृष्ण आपन बचपन बितवले रहलें।
भक्त कवियन के जीवनचरित लिखे वाला नाभादास अपना ग्रंथ भक्तमाल में सूरदास के काव्य प्रतिभा के खूब प्रशंसा कइले बाड़ें। खास तौर पर ऊ कृष्ण के बाल लीला आ दिव्य क्रीड़ा के चित्रण में सूरदास के असाधारण कौशल के सराहना करेलें। सूरदास राम आ सीता पर भी कुछ पद लिखले रहलें, बाकिर उनकर अधिकांश रचना कृष्ण के जीवन, चरित्र आ लीलन पर केंद्रित बा।
=== कबितई ===
सूरदास के कविता के रचना मुख्य रूप से [[ब्रजभाषा]] में बाड़ी स। ओह जमाना में साहित्य के प्रमुख भाषा [[संस्कृत]] आ [[फ़ारसी]] मानल जालीं, जबकि ब्रज भाषा के आम लोग के बोली समझल जाय। सूरदास के रचना सभ ब्रजभाषा के साहित्यिक प्रतिष्ठा दियावे में महत्त्वपूर्ण भूमिका निभवलीं स। उनकर काव्य के कारण ब्रजभाषा के दर्जा एगो साधारण बोली से बढ़ के साहित्य के समृद्ध भाषा के रूप में स्थापित भइल।
सूरदास के नाम से जुड़ल कविता सभ के एकट्ठा रूप से ''[[सूरसागर]]'' कहल जाला, जेकर अर्थ "सूर के सागर" होला। एह नाम के कारण ई बा कि उनका नाम से बहुत बड़हन संख्या में पद आ कविता सभ जुड़ल बाड़ी स। परंपरागत रूप से सूरसागर के बारह भाग में बाँटल जाला, जवन संरचना में [[श्रीमद्भागवत|भागवत पुराण]] से मिलत-जुलत बा। जइसे भागवत पुराण में कृष्ण के जीवन आ लीला सभ के बर्णन मिले ला , ओइसहीं सूरसागर के अधिकांश पद कृष्ण के जीवन, बाल लीला आ भक्ति पर केंद्रित बाड़ें। एह ग्रंथ में मिलल बहुत पद छह से दस गो तुकांत लाइन वाला गीतात्मक रचना के रूप में लिखल गइल हवें।
कृष्ण के अलावा सूरदास के काव्य में राम आ सीता, विष्णु, शिव, आ हिन्दू परंपरा के पात्र जइसे गजेंद्र आ राजा बलि के भी वर्णन मिलेला। एकरे साथे उनका रचना में कवि के निजी आध्यात्मिक संघर्ष, भक्ति भावना आ ईश्वर से आत्मिक जुड़ाव के अभिव्यक्ति भी देखे के मिलेला।
== दर्शन ==
[[वल्लभाचार्य]] के आठ प्रमुख शिष्यन के सामूहिक रूप से ''[[अष्टछाप]]'' कहल जाला। "अष्टछाप" के अर्थ "आठ गो मुहर" भा "आठ गो हस्ताक्षर" होला। एह नाम के संबंध ओह काव्य परंपरा से बा, जहाँ कवि अपना रचना के अंत में आपन छाप (काव्य-हस्ताक्षर) जोड़त रहलें। अष्टछाप के कवियन में सूरदास के सबसे प्रमुख आ श्रेष्ठ कवि मानल जाला।
== पॉपुलर कल्चर में ==
सूरदास के जिनगी पर आधारित कई ठे फिलिम बन चुकल बाड़ी स। एहमें ''सूरदास'' (1939), ''भक्त सूरदास'' (1942), ''संत सूरदास'' (1975) आ ''चिंतामणि सूरदास'' (1988) प्रमुख बाड़ी स।
आँख से आन्हर एगो दूसर भक्त कवि बिल्वमंगल (जिनका के कुछ परंपरन में सूरदास से जोड़ल जाला) आ चिंतामणि के कथा के भारतीय सिनेमा में कई बेर रूपांतरित कइल गइल बा। एह कथा पर आधारित फिल्मन में "बिल्वमंगल" भा "भगत सूरदास" (1919), जेकर निर्देशन रुस्तमजी धोतीवाला कइले रहलें; "बिल्वमंगल" (1932); "चिंतामणि" (1933), जेकर निर्देशन कल्लाकुरी सदाशिव राव कइले रहलें; "चिंतामणि" (1937), जेकर निर्देशन वाई. वी. राव कइले रहलें; "भक्त बिल्वमंगल" (1948), जेकर निर्देशन शांति कुमार कइले रहलें; "बिल्वमंगल" (1954), जेकर निर्देशन डी. एन. माधोक कइले रहलें; "भक्त बिल्वमंगल" (1954), जेकर निर्देशन पिनाकी भूषण मुखर्जी कइले रहलें; "चिंतामणि" (1956), जेकर निर्देशन पी. एस. रामकृष्ण राव कइले रहलें; "चिंतामणि" (1957), जेकर निर्देशन एम. एन. बसवराजैया कइले रहलें; "चिलम्बोली" (1963), जेकर निर्देशन जी. के. रामू कइले रहलें; "बिल्वमंगल" (1976), जेकर निर्देशन गोबिंद राय कइले रहलें; आ "विल्वमंगल की प्रतिज्ञा" (1996), जेकर निर्देशन संजय विरमानी कइले रहलें, शामिल बाड़ी स।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:हिंदी-भाषा के कवि]]
[[श्रेणी:भक्ति आंदोलन]]
[[श्रेणी:संत मत]]
[[श्रेणी:हिंदू संत]]
[[श्रेणी:वैष्णव संप्रदाय]]
{{poet-stub}}
85k21nij49acaaro7t1blwjsgfdy9ma
टेम्पलेट:Infobox religious biography
10
53660
797586
778020
2026-06-09T01:39:12Z
SM7
3953
अपडेट कइल गइल
797586
wikitext
text/x-wiki
{{Infobox|child={{if empty|{{{child|}}}|{{{embed|}}}}}
| bodyclass = biography vcard
| abovestyle = font-size:125%;background:{{if empty|{{{background|}}}|{{Infobox religious building/color|{{{religion|}}}}}}}; color:{{Greater color contrast ratio|{{if empty|{{{background|}}}|{{Infobox religious building/color|{{{religion|}}}}}}}}};
| above = {{#if:{{{honorific prefix|}}}{{{honorific_prefix|}}}{{{honorific-prefix|}}}{{{pre-nominals|}}}|<div class="honorific-prefix" style="font-size: 77%; font-weight: normal;">{{if empty|{{{honorific prefix|}}}|{{{honorific_prefix|}}}|{{{honorific-prefix|}}}|{{{pre-nominals|}}}}}</div>}}<div class="fn">{{if empty|{{{name|}}}|{{PAGENAMEBASE}}}}</div>{{#if:{{{honorific suffix|}}}{{{honorific_suffix|}}}{{{honorific-suffix|}}}{{{post-nominals|}}}|<div class="honorific-suffix" style="font-size: 77%; font-weight: normal;">{{if empty|{{{honorific suffix|}}}|{{{honorific_suffix|}}}|{{{honorific-suffix|}}}|{{{post-nominals|}}}}}</div>}}
| subheaderstyle = font-size:125%; font-weight:bold;
| subheader = {{#if:{{{native-name|}}}{{{native_name|}}}{{{native name|}}}
|<div class="nickname" {{#if:{{{native-name-lang{{if empty|{{{native_name_lang|}}}|{{{native name lang|}}}}}}}}|lang="{{{native-name-lang{{if empty|{{{native_name_lang|}}}|{{{native name lang}}}}}}}}"}}>{{if empty|{{{native-name|}}}|{{{native_name|}}}|{{{native name}}}}}</div>
}}
| image = {{#invoke:InfoboxImage|InfoboxImage|image={{{image|}}}|size={{{image_size|}}}|sizedefault=frameless|upright={{if empty|{{{image_upright|}}}|1}}|alt={{{alt|}}}|suppressplaceholder=yes}}
| caption = {{{caption|}}}
| headerstyle = background:{{if empty|{{{background|}}}|{{Infobox religious building/color|{{{religion|}}}}}}}; color:{{Greater color contrast ratio|{{if empty|{{{background|}}}|{{Infobox religious building/color|{{{religion|}}}}}}}}};
| data1 = {{{module0|}}}
| data2 =
{{Infobox officeholder/office|color={{if empty|{{{background|}}}|{{Infobox religious building/color|{{{religion|}}}}}}}
| office = {{{office1|}}}
| term = {{{term1|}}}
| termstart = {{{term_start1|}}}
| termend = {{{term_end1|}}}
| predecessor = {{{predecessor1|}}}
| successor = {{{successor1|}}}
}}
{{Infobox officeholder/office|color={{if empty|{{{background|}}}|{{Infobox religious building/color|{{{religion|}}}}}}}
| office = {{{office2|}}}
| term = {{{term2|}}}
| termstart = {{{term_start2|}}}
| termend = {{{term_end2|}}}
| predecessor = {{{predecessor2|}}}
| successor = {{{successor2|}}}
}}
{{Infobox officeholder/office|color={{if empty|{{{background|}}}|{{Infobox religious building/color|{{{religion|}}}}}}}
| office = {{{office3|}}}
| term = {{{term3|}}}
| termstart = {{{term_start3|}}}
| termend = {{{term_end3|}}}
| predecessor = {{{predecessor3|}}}
| successor = {{{successor3|}}}
}}
{{Infobox officeholder/office|color={{if empty|{{{background|}}}|{{Infobox religious building/color|{{{religion|}}}}}}}
| office = {{{office4|}}}
| term = {{{term4|}}}
| termstart = {{{term_start4|}}}
| termend = {{{term_end4|}}}
| predecessor = {{{predecessor4|}}}
| successor = {{{successor4|}}}
}}
| label3 = Title
| data3 = {{{title|}}}
| label4 = Official name
| data4 = {{{official_name|}}}
| header6 = {{#if:{{{birth_name|}}}{{{birthname|}}}{{{birth_date|}}}{{{birth_place|}}}{{{nationality|}}}{{{flourished|}}}{{{home_town|}}}{{{partner|}}}{{{spouse|}}}{{{children|}}}{{{parents|}}}{{{citizenship|}}}{{{era|}}}{{{region|}}}{{{party|}}}{{{main interests|}}}{{{notable ideas|}}}{{{notable works|}}}{{{education|}}}{{{known_for|}}}{{{alias|}}}{{{pen_name|}}}{{{posthumous_name|}}}{{{occupation|}}}{{{relations|}}} |Personal life}}
| label7 = Born
| data7 = {{br separated entries
|1={{#if:{{{birth_name|}}}{{{birthname|}}}|<span class="nickname">{{if empty|{{{birth_name|}}}|{{{birthname|}}}}}</span>}}
|2={{#invoke:person date|birth}}
|3={{{pronunciation|}}}
|4={{#if:{{{birth_place|}}}|<div class="birthplace">{{{birth_place|}}}</div>}}
}}
| label8 = Died
| data8 = {{br separated entries
|1={{#invoke:person date|death}}
|2= {{#if:{{{death_place|}}}|<div class="deathplace">{{{death_place|}}}</div>}}
}}
| label9 = Cause of death
| data9 = {{{death_cause|}}}
| label10 = {{#if:{{{cremation_place|}}}|Cremation place|Resting place}}
| data10 = {{#if:{{{cremation_place|}}}
|{{{cremation_place}}}
|{{br separated entries |{{{resting_place|}}} |{{{resting_place_coordinates|}}} }}
}}
| label11 = Buried
| data11 = {{br separated entries
|{{if empty|{{{buried|}}}|{{{tomb|}}}|{{{burial_place|}}}}}
|{{{burial_date|}}}
}}
| class12 = nickname
| label12 = Nationality
| data12 = {{{nationality|}}}
| label13 = Flourished
| data13 = {{{flourished|}}}
| label14 = Home town
| data14 = {{{home_town|}}}
| label15 = {{#if:{{{partner|}}}|Partner|Spouse}}
| data15 = {{if empty|{{{partner|}}}|{{{spouse|}}}}}
| label16 = Children
| data16 = {{{children|}}}
| label17 = Parent{{#if:{{{parents|}}}|{{Pluralize from text|{{{parents|}}}|likely=(s)|plural=s}}|{{#ifexpr:{{count|{{{father|}}}|{{{mother|}}}}} > 1|s}}}}
| data17 = {{#if:{{{parents|}}}
|{{{parents|}}}
|{{#if:{{{father|}}}{{{mother|}}}
|{{unbulleted list|1={{#if:{{{father}}}|{{{father|}}} (father)}}|2={{#if:{{{mother|}}}|{{{mother|}}} (mother)}}}}
}}
}}
| label18 = [[Citizenship]]
| data18 = {{{citizenship|}}}
| label20 = {{#if:{{{era|}}}|Era|Dynasty}}
| data20 = {{if empty|{{{era|}}}|{{{dynasty|}}}}}
| label21 = Region
| data21 = {{{region|}}}
| label29 = [[Political party]]
| data29 = {{if empty|{{{political_party|}}}|{{{political party|}}}|{{{party|}}}}}
| label30 = Main interest(s)
| data30 = {{if empty|{{{main_interests|}}}|{{{main interests|}}}}}
| label31 = Notable idea(s)
| data31 = {{if empty|{{{ideas|}}}|{{{notable_ideas|}}}|{{{notable ideas|}}}}}
| label32 = Notable work(s)
| data32 = {{if empty|{{{works|}}}|{{{notable_works|}}}|{{{notable works|}}}}}
| label33 = {{#if:{{{education|}}}|Education|[[Alma mater|Alma mater]]}}
| data33 = {{if empty|{{{education|}}}|{{{alma_mater|}}}}}
| label36 = Known for
| data36 = {{{known_for|}}}
| class37 = nickname
| label37 = Other names
| data37 = {{if empty|{{{other_names|}}}|{{{other_name|}}}|{{{alias|}}}}}
| class40 = nickname
| label40 = Pen name
| data40 = {{{pen_name|}}}
| class41 = nickname
| label41 = Posthumous name
| data41 = {{{posthumous_name|}}}
| label43 = Occupation
| data43 = {{{occupation|}}}
| label44 = {{#if:{{{relations|}}}|Relations|Relatives}}
| data44 = {{if empty|{{{relations|}}}|{{{relatives|}}}}}
| data45 = {{{misc|}}}
| label46 = {{#if:{{{honors|}}}|Honors|Honours}}
| data46 = {{if empty|{{{honors|}}}|{{{honours|}}}}}
| label47 = Signature
| data47 = {{#invoke:InfoboxImage|InfoboxImage|image={{{signature|}}}|class=skin-invert-image|size={{{signature_size|}}}|sizedefault=150px|alt={{if empty|{{{signature alt|}}}|{{{signature_alt|}}}}}}}
| header67 = {{#if:{{{religion|}}}{{{denomination|}}}{{{temple|}}}{{{order|}}}{{{institute|}}}{{{church|}}}{{{churches|}}}{{{founder|}}}{{{philosophy|}}}|Religious life}}
| class68 = category
| label68 = Religion
| data68 = {{{religion|}}}
| class69 = category
| label69 = Denomination
| data69 = {{{denomination|}}}
| label70 = Temple
| data70 = {{{temple|}}}
| label71 = Order
| data71 = {{{order|}}}
| label72 = Institute
| data72 = {{{institute|}}}
| label73 = Church
| data73 = {{if empty|{{{church|}}}|{{{churches|}}}}}
| label74 = Founder of
| data74 = {{{founder|}}}
| label75 = Philosophy
| data75 = {{{philosophy|}}}
| class76 = category
| label76 = School
| data76 = {{{school|}}}
| label77 = Lineage
| data77 = {{{lineage|}}}
| class78 = category
| label78 = Sect
| data78 = {{br separated entries |{{{sect|}}} |{{{subsect|}}} }}
| class79 = category
| label79 = Jurisprudence
| data79 = {{if empty|{{{Madh'hab|}}}|{{{Maddhab|}}}|{{{jurisprudence|}}}}}
| label80 = Teachers
| data80 = {{{teachers|}}}
| label81 = [[Tariqa]]
| data81 = {{{Sufi_order|}}}
| class82 = category
| label82 = Creed
| data82 = {{if empty|{{{school_tradition|}}}|{{{creed|}}}}}
| label83 = [[Religious movement|Movement]]
| data83 = {{{movement|}}}
| class84 = nickname
| label84 = [[Dharma name]]s
| data84 = {{if empty|{{{dharma_names|}}}|{{{dharma_name|}}}}}
| class85 = nickname
| label85 = Monastic name
| data85 = {{{monastic_name|}}}
| label86 = Profession
| data86 = {{{profession|}}}
| label87 = Ordination
| data87 = {{{ordination|}}}
| label88 = Consecration
| data88 = {{{consecration|}}}
| label90 = Initiation{{#if:{{{initiation_rank|}}}| as {{{initiation_rank|}}}}}
| data90 = {{br separated entries|{{{initiation|}}}|{{{initiation_name|}}}|{{{initiation_date|}}}|{{{initiation_place|}}}|{{#if:{{{initiator|}}}|by {{{initiator|}}}}}}}
| label91 = Initiation{{#if:{{{initiation_rank2|}}}| as {{{initiation_rank2|}}}}}
| data91 = {{br separated entries|{{{initiation_name2|}}}|{{{initiation_date2|}}}|{{{initiation_place2|}}}|{{#if:{{{initiator2|}}}|by {{{initiator2|}}}}}}}
| header92 = {{#if:{{{teacher|}}}{{{guru|}}}{{{location|}}}{{{period|}}}{{{Period|}}}{{{consecration|}}}{{{predecessor|}}}{{{Predecessor|}}}{{{successor|}}}{{{Successor|}}}{{{reincarnation_of|}}}{{{reincarnation of|}}}{{{reason|}}}{{{Reason|}}}{{{disciple_of|}}}
{{{disciples|}}}{{{students|}}}{{{influences|}}}{{{influenced|}}}{{{awards|}}}{{{literary_works|}}}{{{previous_post|}}}{{{previous post|}}}{{{present_post|}}}{{{present post|}}}{{{post|}}}|{{#switch:{{{religion|}}}|[[Hinduism]]|[[Jainism]]|[[Sikhism]]=Religious career|[[Judaism]]=Jewish leader|[[Islam]]=Muslim leader|#default=Senior posting}} }}
| label93 = {{#if:{{{guru|}}}|Guru|Teacher}}
| data93 = {{if empty|{{{guru|}}}|{{{teacher|}}}}}
| class94 = label
| label94 = Based in
| data94 = {{{location|}}}
| label95 = Present post
| data95 = {{if empty|{{{present_post|}}}|{{{present post|}}}}}
| label96 = Post
| data96 = {{{post|}}}
| label97 = Period in office
| data97 = {{if empty|{{{period|}}}|{{{Period|}}}}}
| label98 = Predecessor
| data98 = {{if empty|{{{predecessor|}}}|{{{Predecessor|}}}}}
| label99 = Successor
| data99 = {{if empty|{{{successor|}}}|{{{Successor|}}}}}
| label100 = Reason for exit
| data100 = {{if empty|{{{reason|}}}|{{{Reason|}}}}}
| label101 = Previous post
| data101 = {{if empty|{{{previous_post|}}}|{{{previous post|}}}}}
| label102 = [[Reincarnation]]
| data102 = {{if empty|{{{reincarnation_of|}}}|{{{reincarnation of|}}}}}
| label103 = Disciple of
| data103 = {{{disciple_of|}}}
| data104 = {{#if:{{{disciples|}}}
| {{Collapsible list
| expand = {{{expand_disciples|}}}
| title = Disciples
| frame_style = border:none; padding:0;
| list_style = text-align:center;
| 1 = {{{disciples}}}
}}
}}
| data105 = {{#if:{{{students|}}}
| {{Collapsible list
| expand = {{{expand_students|}}}
| title = Students
| frame_style = border:none; padding:0;
| list_style = text-align:center;
| 1 = {{{students}}}
}}
}}
| label106 = {{if empty|{{{initiated_label|}}}|Initiated}}
| data106 = {{{initiated|}}}
| data107 = {{#if:{{{influences|}}}
| {{Collapsible list
| expand = {{{expand_influences|}}}
| title = Influenced by
| frame_style = border:none; padding:0;
| list_style = text-align:center;
| 1 = {{{influences}}}
}}
}}
| data108 = {{#if:{{{influenced|}}}
| {{Collapsible list
| expand = {{{expand_influenced|}}}
| title = Influenced
| frame_style = border:none; padding:0;
| list_style = text-align:center;
| 1 = {{{influenced}}}
}}
}}
| label109 = Awards
| data109 = {{{awards|}}}
| label110 = Position
| data110 = {{{synagogueposition|}}}
| class110 = org
| label111 = Synagogue
| data111 = {{{synagogue|}}}
| label112 = Position
| data112 = {{{yeshivaposition|}}}
| label113 = Yeshiva
| data113 = {{{yeshiva|}}}
| label114 = Position
| data114 = {{if empty|{{{organizationposition|}}}|{{{organisationposition|}}}}}
| class114 = label
| label115 = {{#if:{{{organization|}}}|Organization|Organisation}}
| data115 = {{if empty|{{{organization|}}}|{{{organisation|}}}}}
| label116 = Began
| data116 = {{{began|}}}
| rowclass116 = note
| label117 = Ended
| data117 = {{{ended|}}}
| rowclass117 = note
| label118 = Main work
| data118 = {{{main_work|}}}
| label119 = Other
| data119 = {{{other_post|}}}
| label120 = ''[[Yahrtzeit]]''
| data120 = {{{yahrtzeit|}}}
| label122 = Residence
| data122 = {{{residence|}}}
| class122 = label
| label123 = Dynasty
| data123 = {{{dynasty|}}}
| label124 = [[Semikhah]]
| data124 = {{{semicha|}}}
| header129 = {{#if:{{{nickname|}}}{{{allegiance|}}}{{{branch|}}}{{{serviceyears|}}}{{{rank|}}}{{{unit|}}}{{{commands|}}}{{{battles|}}}{{{mawards|}}}{{{military_blank1|}}}|Military service}}
| label130 = Nickname(s)
| data130 = {{{nickname|}}}
| label131 = Allegiance
| data131 = {{{allegiance|}}}
| label132 = Service/branch
| data132 = {{{branch|}}}
| label133 = Years of service
| data133 = {{{serviceyears|}}}
| label134 = Rank
| data134 = {{if empty|{{{rank|}}}|{{{Rank|}}}}}
| label135 = Unit
| data135 = {{{unit|}}}
| label136 = Commands
| data136 = {{{commands|}}}
| label137 = Battles/wars
| data137 = {{{battles|}}}
| label138 = {{#if:{{{awards|}}}|Military awards|Awards}}
| data138 = {{{mawards|}}}
| label139 = {{{military_blank1}}}
| data139 = {{{military_data1|}}}
| label140 = {{{military_blank2}}}
| data140 = {{{military_data2|}}}
| label141 = {{{military_blank3}}}
| data141 = {{{military_data3|}}}
| label142 = {{{military_blank4}}}
| data142 = {{{military_data4|}}}
| label143 = {{{military_blank5}}}
| data143 = {{{military_data5|}}}
| data144 = {{{module|}}}
| data145 = {{{module1|}}}
| data146 = {{{module2|}}}
| data147 = {{{module3|}}}
| data148 = {{{module4|}}}
| data149 = {{{module5|}}}
| label159 = Website
| data159 = {{{website|}}}
| belowstyle = text-align:left; border-top:1px #aaa solid;
| below = {{{footnotes|}}}
}}{{#if:{{{dharma_names|}}}{{{dharma_name|}}}{{{dharma name|}}}{{{monastic_name|}}}{{{monastic name|}}}{{{posthumous_name|}}}{{{posthumous name|}}}{{{pen_name|}}}{{{pen name|}}}|[[Category:Pages using religious biography with multiple nickname parameters]]}}<includeonly>{{#ifeq:{{if empty|{{{child|}}}|{{{embed|}}}}}|yes||{{Wikidata image|1={{{image|}}}|2={{{nocat_wdimage|}}}}}}}</includeonly>{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox religious biography with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox religious biography]] with unknown parameter "_VALUE_"|ignoreblank=y| alias | allegiance | alma_mater | alt | awards | background | battles | began | birth_date | birth_name | birth_place | birthname | branch | burial_date | burial_place | buried | caption | child | children | church | churches | citizenship | commands | consecration | creed | cremation_place | death_cause | death_date | death_place | denomination | dharma name | dharma_name | dharma_names | disciple_of | disciples | dynasty | education | embed | ended | era | expand_disciples | expand_influenced | expand_influences | expand_students | father | flourished | footnotes | founder | guru | home_town | honorific prefix | honorific suffix | honorific_prefix | honorific_suffix | honorific-prefix | honorific-suffix | honors | honours | ideas | image | image_size | image_upright | influenced | influences | initiated | initiated_label | initiation | initiation_date | initiation_date2 | initiation_name | initiation_name2 | initiation_place | initiation_place2 | initiation_rank | initiation_rank2 | initiator | initiator2 | institute | jurisprudence | known_for | lineage | literary_works | location | Maddhab | Madh'hab | main interests | main_interests | main_work | mawards | military_blank1 | military_blank2 | military_blank3 | military_blank4 | military_blank5 | military_data1 | military_data2 | military_data3 | military_data4 | military_data5 | misc | module | module0 | module1 | module2 | module3 | module4 | module5 | monastic name | monastic_name | mother | movement | name | nationality | native name | native name lang | native_name | native_name_lang | native-name | native-name-lang | nickname | nocat_wdimage | notable ideas | notable works | notable_ideas | notable_works | occupation | office1 | office2 | office3 | office4 | official_name | order | ordination | organisation | organisationposition | organization | organizationposition | other_name | other_names | other_post | parents | partner | party | pen name | pen_name | Period | period | philosophy | political party | political_party | post | post-nominals | posthumous name | posthumous_name | pre-nominals | Predecessor | predecessor | predecessor1 | predecessor2 | predecessor3 | predecessor4 | present post | present_post | previous post | previous_post | profession | pronunciation | rank | Rank | Reason | reason | region | reincarnation of | reincarnation_of | relations | relatives | religion | residence | resting_place | resting_place_coordinates | school | school_tradition | sect | semicha | serviceyears | signature | signature alt | signature_alt | signature_size | spouse | students | subsect | Successor | successor | successor1 | successor2 | successor3 | successor4 | Sufi_order | synagogue | synagogueposition | teacher | teachers | temple | term_end1 | term_end2 | term_end3 | term_end4 | term_start1 | term_start2 | term_start3 | term_start4 | term1 | term2 | term3 | term4 | title | tomb | unit | website | works | yahrtzeit | yeshiva | yeshivaposition }}{{#if:{{{religion|}}}||{{main other|[[Category:Pages using infobox religious biography without religion parameter]]}} }}{{#invoke:Check for conflicting parameters|check
| template = [[Template:Infobox religious biography]]
| cat = {{main other|Category:Pages using infobox religious biography with conflicting parameters}}
| child; embed
| honorific prefix; honorific_prefix; honorific-prefix; pre-nominals
| honorific suffix; honorific_suffix; honorific-suffix; post-nominals
| native_name_lang; native name lang
| native-name; native_name; native name
| birth_name; birthname
| buried; tomb; burial_place
| partner; spouse
| era; dynasty
| political_party; political party; party
| main_interests; main interests
| ideas; notable_ideas; notable ideas
| works; notable_works; notable works
| education; alma_mater
| other_names; other_name; alias
| relations; relatives
| honors; honours
| signature alt; signature_alt
| church; churches
| Maddhab; Madh'hab; jurisprudence
| school_tradition; creed
| dharma_names; dharma_name
| guru; teacher
| present_post; present post
| period; Period
| predecessor; Predecessor
| successor; Successor
| reason; Reason
| previous_post; previous post
| reincarnation_of; reincarnation of
| organizationposition; organisationposition
| organization; organisation
| rank; Rank
}}<noinclude>
{{Documentation}}
</noinclude>
4psmxjgtrl9ss94m6we9kgdnu4fce7v
इस्कूल
0
83797
797113
781654
2026-06-08T20:06:29Z
SM7
3953
फोटो जोड़ल गइल, +विकिकड़ी जोड़ल गइल
797113
wikitext
text/x-wiki
[[चित्र:Perumacheri AUP School.JPG|thumb|केरल के देहाती इलाका में एगो इस्कूल के बिल्डिंग]]
'''इस्कूल''' ({{Lang|en|school}}; [[हिंदी भाषा|हिंदी]]: विद्यालय; [[उर्दू]]: मदरसा) एक किसिम के [[शिक्षा]] देवे वाला संस्थान होला जे लड़िकन के पढ़ाई करे खातिर जगह आ माहौल देवे खातिर होला, जहाँ बिद्यार्थी मास्टर साहब लोगन द्वारा कुछ सीख-पढ़ सके। अधिकतर देसन में लड़िका-लड़िकिन के पढ़ाई-लिखाई खातिर औपचारिक सिस्टम होखे ला, कई जगहा त ई अनिवार्य (कंपलसरी) होला। एह पढ़ाई लिखाई के औपचारिक सिस्टम में बिद्यार्थी छोट लेवल से ऊपर के लेवल ले कई इस्कूल सभ से हो के आगे बढ़े ला आ आपन शिक्षा पूरा करे ला। अलग-अलग देसन में कुछ हेरफेर के साथ आ अलग-अलग नाँव से अइसन शिक्षण संस्थान होखे लें। जइसे कि छोट लड़िकन खातिर [[प्राइमरी इस्कूल]], ओकरा बाद [[सेकेंडरी इस्कूल]] आ एही तरीका से आगे [[कॉलेज]] आ [[यूनिवर्सिटी]] ले के पूरा बेवस्था होखे ले जेकरा से गुजर के निचला दर्जा से ले के ऊँच दर्जा तक ले के शिक्षा हासिल कइल जा सके ला।
{{clear}}
[[श्रेणी:शिक्षा]]
{{education-stub}}
ovyr06jm3a7it82skwv4suvgvql129j
शिक्षाशास्त्र
0
83899
797112
781660
2026-06-08T20:03:00Z
SM7
3953
अंग्रेजी विकिपीडिया से अनुबाद क के बिस्तार कइल गइल
797112
wikitext
text/x-wiki
{{distinguish|शिक्षा बिज्ञान|एजुकेशन}}
'''शिक्षाशास्त्र''' ({{Lang|en|Pedagogy}}, {{IPAc-en|ˈ|p|ɛ|d|ə|ɡ|ɒ|dʒ|i|,_|-|ɡ|oʊ|dʒ|i|,_|-|ɡ|ɒ|ɡ|i}}; ''पेड'गोजी'') पढ़ावे (टीचिंग) के कला, बिज्ञान आ पेशा हवे।<ref>{{cite web |title=Definition of PEDAGOGY |url=https://www.merriam-webster.com/dictionary/pedagogy |website=www.merriam-webster.com |access-date=17 फरवरी 2022 |language=en}}</ref> ई ज्ञान-बिज्ञान के शाखा आ अध्ययन के बिसय हवे जेह में पढ़ाई, पढ़ावे के तरीका, सीखल आ सीखे के तरीका; आ इनहन पर बिबिध सामाजिक, मनोबैज्ञानिक तत्वन के परभाव वगैरह के अध्ययन कइल जाला। एक दूसर नाँव '''एजुकेशन''' चाहे '''एडुकेशन''' (education) हवे;<ref>{{cite web |title=Definition of EDUCATION |url=https://www.merriam-webster.com/dictionary/education |website=www.merriam-webster.com |access-date=17 फरवरी 2022 |language=en}}</ref> हालाँकि [[शिक्षा बिज्ञान]] भा एजुकेशन साइंस के आधुनिक समय में पेडागोजी से ब्यापक बिसय मानल जाला।
पेडागोजी के आम परिभाषा दिहल जाला — पढ़ावे के बिधी के अध्ययन, जेह में शिक्षा के मकसद आ एह मकसद सभ के हासिल करे के तरीकवो सामिल होखें।<ref>{{cite web |title=pedagogy {{!}} Methods, Theories, & Facts {{!}} Britannica |url=https://www.britannica.com/science/pedagogy |website=www.britannica.com |access-date=17 फरवरी 2022 |language=en |quote='''pedagogy''', the study of teaching methods, including the aims of education and the ways in which such goals may be achieved. |url-status=live}}</ref> पेडागॉजी के अक्सरहा पढ़ावे-लिखावे के कला आ प्रक्रिया (आर्ट आ प्रॉसेस) के रूप में वर्णित कइल जाला। शिक्षक जवन शिक्षण पद्धति अपनावेलन, ऊ उनका के पढ़ावे के तरीका, निर्णय आ शिक्षण रणनीति के आकार देले। एह प्रक्रिया में सीखला के सिद्धांत, विद्यार्थी के जरूरत, समझ, पृष्ठभूमि आ व्यक्तिगत रुचि जइसन बातन के ध्यान में रखल जाला।शिक्षाशास्त्र के मकसद ब्यापक हो सकेला। ई एक ओर उदार शिक्षा (लिबरल एजुकेशन) के माध्यम से मनुष्य के समग्र विकास पर जोर दे सकेला, त दुसरे ओर व्यावसायिक शिक्षा (वोकेशनल एजुकेशन) के तहत खास कौशल सिखावे आ सीखे पर केंद्रित हो सकेला।
शिक्षण रणनीति विद्यार्थी के पहिले से मौजूद ज्ञान, अनुभव, परिस्थिति, वातावरण आ शिक्षक-विद्यार्थी द्वारा निर्धारित सीखला के लक्ष्य के आधार पर तय कइल जाला। एह तरह के शिक्षण पद्धति के एगो प्रसिद्ध उदाहरण सुकराती पद्धति (सॉक्रेटिक मेथड) हवे, जवना में सवाल-जवाब के माध्यम से ज्ञान आ समझ विकसित कइल जाला।
{{clear}}
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:शिक्षाशास्त्र| ]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:शिक्षा| ]]
{{Education-stub}}
1ni6xyn3443qtalpaoph2otuq0rz5i5
797167
797112
2026-06-08T20:25:19Z
SM7
3953
797167
wikitext
text/x-wiki
{{distinguish|शिक्षा बिज्ञान|एजुकेशन}}
'''शिक्षाशास्त्र''' ({{Lang|en|Pedagogy}}, {{IPAc-en|ˈ|p|ɛ|d|ə|ɡ|ɒ|dʒ|i|,_|-|ɡ|oʊ|dʒ|i|,_|-|ɡ|ɒ|ɡ|i}}; ''पेड'गोजी'') पढ़ावे (टीचिंग) के कला, बिज्ञान आ पेशा हवे।<ref>{{cite web |title=Definition of PEDAGOGY |url=https://www.merriam-webster.com/dictionary/pedagogy |website=www.merriam-webster.com |access-date=17 फरवरी 2022 |language=en}}</ref> ई ज्ञान-बिज्ञान के शाखा आ अध्ययन के बिसय हवे जेह में पढ़ाई, पढ़ावे के तरीका, सीखल आ सीखे के तरीका; आ इनहन पर बिबिध सामाजिक, मनोबैज्ञानिक तत्वन के परभाव वगैरह के अध्ययन कइल जाला। एक दूसर नाँव '''एजुकेशन''' चाहे '''एडुकेशन''' (education) हवे;<ref>{{cite web |title=Definition of EDUCATION |url=https://www.merriam-webster.com/dictionary/education |website=www.merriam-webster.com |access-date=17 फरवरी 2022 |language=en}}</ref> हालाँकि [[शिक्षा बिज्ञान]] भा एजुकेशन साइंस के आधुनिक समय में पेडागोजी से ब्यापक बिसय मानल जाला।
पेडागोजी के आम परिभाषा दिहल जाला — पढ़ावे के बिधी के अध्ययन, जेह में शिक्षा के मकसद आ एह मकसद सभ के हासिल करे के तरीकवो सामिल होखें।<ref>{{cite web |title=pedagogy {{!}} Methods, Theories, & Facts {{!}} Britannica |url=https://www.britannica.com/science/pedagogy |website=www.britannica.com |access-date=17 फरवरी 2022 |language=en |quote='''pedagogy''', the study of teaching methods, including the aims of education and the ways in which such goals may be achieved. |url-status=live}}</ref> पेडागॉजी के अक्सरहा पढ़ावे-लिखावे के कला आ प्रक्रिया (आर्ट आ प्रॉसेस) के रूप में वर्णित कइल जाला। शिक्षक जवन शिक्षण पद्धति अपनावेलन, ऊ उनका के पढ़ावे के तरीका, निर्णय आ शिक्षण रणनीति के आकार देले। एह प्रक्रिया में सीखला के सिद्धांत, विद्यार्थी के जरूरत, समझ, पृष्ठभूमि आ व्यक्तिगत रुचि जइसन बातन के ध्यान में रखल जाला।शिक्षाशास्त्र के मकसद ब्यापक हो सकेला। ई एक ओर उदार शिक्षा (लिबरल एजुकेशन) के माध्यम से मनुष्य के समग्र विकास पर जोर दे सकेला, त दुसरे ओर व्यावसायिक शिक्षा (वोकेशनल एजुकेशन) के तहत खास कौशल सिखावे आ सीखे पर केंद्रित हो सकेला।
शिक्षण रणनीति विद्यार्थी के पहिले से मौजूद ज्ञान, अनुभव, परिस्थिति, वातावरण आ शिक्षक-विद्यार्थी द्वारा निर्धारित सीखला के लक्ष्य के आधार पर तय कइल जाला। एह तरह के शिक्षण पद्धति के एगो प्रसिद्ध उदाहरण सुकराती पद्धति (सॉक्रेटिक मेथड) हवे, जवना में सवाल-जवाब के माध्यम से ज्ञान आ समझ विकसित कइल जाला।
=== शिक्षण के बिधि ===
{{Excerpt|शिक्षण बिधि|paragraphs=1|only=paragraphs}}
{{clear}}
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:शिक्षाशास्त्र| ]]
[[श्रेणी:सामाजिक बिज्ञान]]
[[श्रेणी:शिक्षा| ]]
{{Education-stub}}
rynbsf0na36trultudsq25dym3z5ac3
मुगलसराय जंक्शन रेलवे स्टेशन
0
88222
797098
773815
2026-06-08T14:24:18Z
SM7
3953
सफाई कइल गइल
797098
wikitext
text/x-wiki
{{Infobox station
| name=Mughalsarai Junction<br />{{small|Pt. Deen Dayal Upadhyaya Junction}}
| native_name_lang=
| style=Indian Railways
| type=[[Express train|Express]] and [[Passenger train|passenger]] station
| image = Deendayalmughalsaraijn.png
| image_size =
| image_caption = पं. दीन दयाल उपाध्याय जंक्शन रेलवे स्टेशन
| address=[[Mughalsarai]] – 232101, [[Uttar Pradesh]]
| country=India
| coordinates={{Coord|25.2767|N|83.1173|E|type:railwaystation_region:IN|format=dms|display=inline,title}}
| elevation={{convert|79.273|m|ft}}
| owned=[[Indian Railways]]
| operator=[[East Central Railway zone|East Central Railways]]
| lines=[[Howrah–Delhi main line]],<br />[[Howrah–Gaya–Delhi line]],<br />[[Howrah–Allahabad–Mumbai line]],<br />[[Gaya–Mughalsarai section]],<br />[[Mughalsarai–Kanpur section]],<br />[[Grand Chord]],<br />[[Patna–Mughalsarai section]],<br />Mughalsarai–Varanasi–Lucknow section
| platforms=8
| tracks=23
| connections=Auto stand
| ADA=
| bicycle=Yes
| parking=Yes
| structure=Standard on ground
| code={{Indian railway code
| code = DDU
| zone = [[East Central Railway zone]]
| division = {{rwd| Mughalsarai}}
}}
status=Functioning
opened={{start date and age|df=yes|1862}}
rebuilt=
electrified=1961–63
passengers=3 lakh passengers per day
pass_system=
pass_year=
pass_percent=
map_type=India Uttar Pradesh
map_dot_label=Mughalsarai Junction
map_caption=Location in [[Uttar Pradesh]]
}}
[[Category:Articles using Infobox station with markup inside name]]
'''मुगलसराय जंक्शन''', आधिकारिक तौर पर '''पं. दीन दयाल उपाध्याय जंक्शन''', (स्टेशन कोड: '''डीडीयू''', पहिले '''एमजीएस''' ) भारत के [[उत्तर प्रदेश]] राज्य के [[मुगलसराय]] शहर में एगो रेलवे स्टेशन बा। <ref>{{Cite news|url=https://www.indiatoday.in/education-today/gk-current-affairs/story/mughalsarai-station-renamed-1306483-2018-08-06|title=After 156 years, Mughalsarai station renamed as Pandit Deen Dayal Upadhyaya Junction: Know all about it|date=6 August 2018|work=[[India Today]]|access-date=17 June 2021|language=en}}</ref> एह स्टेशन पर एशिया के सभसे बड़हन रेलवे मार्शलिंग यार्ड बा। <ref>{{Cite web|url=https://www.irfca.org/faq/faq-yard.html|title=[IRFCA] Indian Railways FAQ: Freight Sheds and Marshalling Yards|publisher=IRFCA|access-date=7 January 2020}}</ref> मुगलसराय यार्ड में एक महीना में लगभग 450–500 ट्रेन के सुविधा मिलेला। <ref>{{Cite news|url=https://www.livemint.com/Politics/JnG2X7OAKevymvTvGEthOK/Railways-to-invest-Rs3000-crore-to-mechanize-automate-yard.html|title=Railways to invest Rs3,000 crore to mechanize, automate yards|last=Sood|first=Jyotika|date=17 October 2017|work=[[Mint (newspaper)|Mint]]|access-date=1 February 2021|language=en}}</ref> प्रीमियम श्रेणी के पूरब ओर जाए वाली राजधानी ट्रेन आ दुरोंतो ट्रेन समेत सगरी ट्रेन रुक जाले (एहसे पूरा भारतीय रेलवे नेटवर्क में एकरा के अनोखा बनावेला; जवन एकरा के प्रयागराज जंक्शन, भोपाल जंक्शन, आगरा कैंट, ग्वालियर जंक्शन, खड़गपुर, नागपुर आदि जइसन अन्य प्रमुख रेलवे स्टेशनन से अलग करेला) एह स्टेशन पर बा। मुगलसराय में प्रमुख इंस्टालेशन में 147 इंजन वाला इलेक्ट्रिक इंजन शेड, 53 इंजन वाला डीजल इंजन शेड, वैगन आरओएच शेड अउरी 169 बेड के डिवीजनल अस्पताल शामिल बा।
== इतिहास ==
ईस्ट इंडियन रेलवे कंपनी दिल्ली आ हावड़ा के जोड़े के काम उन्नीसवीं सदी के मध्य से शुरू कइलस। कराची के लगे (अब पाकिस्तान में) गद्दर के बाद ई दुसरा सभसे बड़ रेलवे स्टेशन रहल जे 1862 में ब्रिटिश शासन के दौरान बनल। परसिद्ध रूप से पूरबी भारत के प्रवेश द्वार के रूप में जानल जाए वाला ई जंक्शन दिल्ली-कलकत्ता मार्ग के जोड़े के प्रोजेक्ट के हिस्सा के रूप में ब्रिटिश रेलवे कंपनी द्वारा बनावल गइल रहे जेकरा के ईस्ट इंडियन रेलवे के नाँव से जानल जाला।
ई स्टेशन ग्रांड ट्रंक रोड मार्ग पर स्थित बा। ई मुगल जमाना के सबसे व्यस्त गलियारा में से एगो रहे जवन पूरबी भारत के उत्तर से जोड़त रहे। 1862 में रेलवे के पटरी मुगलसराय के पार क के [[जमुना|यमुना]] के पच्छिमी किनारे पहुँचल।<ref>{{Cite news|url=https://timesofindia.indiatimes.com/city/varanasi/the-many-names-of-mughalsarai/articleshow/59924547.cms|title=Mughalsarai: The many names of Mughalsarai|last=Dikshit|first=Rajeev|date=5 August 2017|work=[[The Times of India]]|access-date=1 February 2021|language=en}}</ref> दिल्ली के थ्रू लिंक के स्थापना 1866 में भइल <ref>{{Cite web|url=http://www.irfca.org/faq/faq-hist.html|title=IR History: Early History (1832–1869)|publisher=IRFCA|access-date=19 June 2013}}</ref> ग्रांड कॉर्ड के कमीशन 1906 में भइल। <ref>{{Cite web|url=http://www.irfca.org/faq/faq-history3.html|title=IR History: Part III (1900–1947)|publisher=IRFCA|access-date=19 June 2013}}</ref>
[[गंगा नदी|गंडक]] के पार [[राजघाट पुल|डफरिन पुल]] 1887 में खुलल रहे जवन मुगलसराय के [[बनारस कैंट रेलवे स्टेशन|वाराणसी]] से जोड़े ला।<ref>{{Cite web|url=http://www.irfca.org/faq/faq-history2.html|title=IR History: Part II (1870–1899)|publisher=IRFCA|access-date=19 June 2013}}</ref>
== नाँव बदलाव ==
ग्रांड ट्रंक रोड पर स्थापित ई स्टेशन एगो रोचक अतीत के सहेजे ला। शेर शाह सूरी द्वारा बनावल गइल ई सड़क अधिकतर कारवां सभ खातिर मुख्य मारग के काम कइलस, मध्यकालीन दौर में आ बहुत बाद में भी, पूर्वी भा दक्खिन भारत से उत्तर भारत के ओर यात्रा कइलस। जेतना व्यस्त रहे, आ अबहियों बा, सड़क के दुनो ओर कई गो सराय रहली स आ एही से एकर नाम — मुगलसराय पड़ल।
भारतीय जनसंघ के अध्यक्ष चुनला गइला के मुश्किल से दू महीना बाद 10 फरवरी 1968 के सांझ के दीन दयाल उपाध्याय लखनऊ से पटना खातिर सीयालदह एक्सप्रेस में सवार भइलन। कुछ घंटा बाद मुगलसराय स्टेशन प एगो प्लेटफार्म के छोर से कुछ सौ फीट दूर एगो खंभा के लगे उनुकर लाश मिलल।
एकरा बाद जवन भइल उ एगो लंबा अउरी शामिल जांच रहे कि संघ जवना प जोर देले रहे कि उ राजनीतिक मकसद से भइल हत्या ह। सीबीआई के जांच एकरा के दुर्घटना कहलस; दू गो आदमी डकैती के कोशिश में उनुकरा के ट्रेन से बाहर धकेले के कबूल कइले बाकिर सबूत के कमी का चलते बरी कर दिहल गइल; उपाध्याय के शरीर पर कवनो संघर्ष भा चोट के निशान ना रहे। आ संघ में सत्ता के आंतरिक लड़ाई का बारे में षड्यंत्र सिद्धांत आजुओ भरपूर बा। 1992 में [[उत्तर प्रदेश]] राज्य के तत्कालीन सरकार मुगलसराय के नाँव [[दीनदयाल उपाध्याय|दीन दयाल उपाध्याय]] के नाँव पर रखे के कोसिस कइलस हालाँकि, बाबरी महजिद ध्वंस के बाद राज्य में हिंसा के प्रकोप के बाद मुख्यमंत्री कल्याण सिंह के इस्तीफा देवे के पड़ला पर ई योजना ठंडा बस्ता में हो गइल।<ref name="rename">{{Cite news|url=https://www.indiatoday.in/india/story/mughalsarai-station-now-deen-dayal-upadhyay-1305833-2018-08-05|title=Mughalsarai station is now Deen Dayal Upadhyay station|date=5 August 2018|work=[[India Today]]|access-date=21 August 2018}}</ref> 2017 [[भारत सरकार|में भारत सरकार]] [[आदित्यनाथ|योगी आदित्यनाथ]] के नेतृत्व में राज्य सरकार द्वारा भेजल गइल एगो नया प्रस्ताव के मंजूरी दिहलस जवना में स्टेशन के नाम बदल दिहल गइल। <ref>{{Cite news|url=https://indianexpress.com/article/india/here-are-the-railway-stations-which-have-been-renamed-recently-4781759/|title=Mughalsarai railway station renamed after Deen Dayal Upadhyaya: A look at stations that have been renamed recently|date=4 August 2017|work=[[The Indian Express]]|access-date=21 August 2018}}</ref> एह स्टेशन के आधिकारिक रूप से 4 जून 2018 के नाँव बदल के पंडित दीन दयाल उपाध्याय जंक्शन रखल गइल। <ref name="rename" />
== विद्युतीकरण ==
गया–मुगलसराय जंक्शन सेक्टर के विद्युतीकरण 1961-63 में भइल। मुगलसराय यार्ड में 1963-65 में विद्युतीकरण भइल। <ref>{{Cite web|url=http://irfca.org/docs/electrification-history.html|title=History of Electrification|publisher=IRFCA|access-date=19 June 2013}}</ref>
== मार्शलिंग यार्ड ==
मुगलसराय मार्शलिंग यार्ड एशिया के सबसे बड़ बा। <ref name="marshalling">{{Cite web|url=http://www.irfca.org/faq/faq-yard.html|title=Freight Sheds and Mashalling Yards|publisher=IRFCA|access-date=19 June 2013}}</ref> <ref>{{Cite web|url=http://www.ecr.indianrailways.gov.in/uploads/files/1322119784210-Inf.pdf|title=General Information|publisher=East Central Railway|access-date=19 June 2013}}</ref> <ref>{{Cite web|url=http://www.outlookindia.com/article.aspx?210638|title=Mughalsarai: Tracks to Nowhere|publisher=Outlook India, 8 January 2001|access-date=19 June 2013}}</ref> ई 12.5 किमी लंबा बाटे आ डेली लगभग 1,500 वैगन के संभालेला। रेलवे के ओर से टुकड़ा-टुकड़ा लोडिंग बंद होखला के बाद वैगन हैंडलिंग में गिरावट आइल बा। अपना चरम पर ई रोज 5,000 वैगन के संभालत रहे। भारतीय रेलवे पर मौजूद सगरी डिवीजनन में से मुगलसराय डिवीजन में सबसे जादा ट्रेन के संचालन होला – गुड्स आ कोचिंग दुनु के। ई भारत के पूरबी हिस्सा आ उत्तरी हिस्सा के बीच के पुल हवे। ई पिट हेड कोयला आ पावर हाउस, उपयोगकर्ता लोगन खातिर तैयार स्टील उत्पाद, देश के पूर्वी हिस्सा से खाद्य अनाज आ खाद आ उद्योगन के अन्य कच्चा माल के बीच के दूरी कम करे ला। पूर्वी मध्य रेलवे के दक्षता के निर्धारण में डिवीजन के परिचालन दक्षता के अहम भूमिका होला आ एह डिवीजन पर संचालन में कवनो तरह के झटका भा अक्षमता एगो संवेदनशील मामला बा जवन रेलवे के समग्र संचालन के प्रभावित करेला। अपना बहुते महत्व का चलते रेलवे बोर्ड मुगलसराय संभाग के कामकाज पर खास नजर राखेला. <ref name="marshalling" /> <ref>{{Cite web|url=http://indianrailwayemployee.com/node/6016|title=Marshalling Yards|publisher=Indian Railway Employee|access-date=19 June 2013|archive-date=15 January 2013|archive-url=https://web.archive.org/web/20130115212621/http://indianrailwayemployee.com/node/6016|url-status=dead}}</ref>
== शेड आ वर्कशाप ==
मुगल सराय डीजल लोको शेड में WDM-2, WDM-3A आ WDS-5 डीजल लोको के घर बा। डीजल शेड में 50 इलेक्ट्रिक लोको भी बा, जवन कि सभ WAG-7 बा। मुगलसराय में उत्तरी रेलवे के डीजल लोको शेड रहे। एकरा के 2001 में बंद कर दिहल गइल। मुगलसराय इलेक्ट्रिक लोको शेड में 150 से अधिका इलेक्ट्रिक लोको राखल जा सकेलें। एहमें वैप-4 आ 70 से अधिका वैग-7 लोको शामिल बाड़ें। इलेक्ट्रिक शेड में हाल ही में वैग-9 इंजन राखल शुरू हो गइल बा।
भारतीय रेलवे के सबसे बड़ वैगन मरम्मत कार्यशाला मुगलसराय में बा। <ref name="sheds">{{Cite web|url=http://www.irfca.org/faq/faq-shed.html|title=Sheds and workshops|publisher=IRFCA|access-date=19 June 2013}}</ref>
== यात्री लोग के आवाजाही ==
पं. दीन दयाल उपाध्याय जंक्शन भारतीय रेलवे के टॉप सौ बुकिंग स्टेशनन में शामिल बा।<ref>{{Cite web|url=http://www.indianrail.gov.in/7days_Avl.html|title=Indian Railways Passenger Reservation Enquiry|website=Availability in trains for Top 100 Booking Stations of Indian Railways|publisher=IRFCA|archive-url=https://web.archive.org/web/20140510115649/http://www.indianrail.gov.in/7days_Avl.html|archive-date=10 May 2014|access-date=19 June 2013}}</ref>
== सुविधा सभ ==
पं. दीन दयाल उपाध्याय जंक्शन रेलवे स्टेशन में 2 गो एसी कमरा, 4 गो गैर-एसी रिटायरिंग कमरा, आ दस बेड वाला गैर-एसी डोरमेटरी बा। एकरा में फूड प्लाजा आ 'जन आहर' (सस्ती खाना) के सुविधा बा। एह स्टेशन पर राष्ट्रीयकृत बैंकन के एटीएम बा।<ref>{{Cite web|url=http://www.ecr.indianrailways.gov.in/uploads/files/1327473148418-Organizational%20Information-JAN-12.pdf|title=Mughalsarai Division, Commercial Department|publisher=Indian Railways|access-date=19 June 2013}}</ref>
== गैलरी ==
<gallery widths="180">
चित्र:Pt. Deen Dayal Upadhyaya Junction board.jpg|Board of the Pt. Deen Dayal Upadhyaya Junction.
चित्र:Food Track at Mughalsarai Junction platform 6.jpg|Food Track at Mughalsarai Junction platform 6
चित्र:Howrah-New Delhi Duronto Express on Platform 6 of Mughalsarai Junction.jpg|12273 Howrah-New Delhi Duronto Express on Platform 6 of Mughalsarai Junction
चित्र:Upper Class waiting room at Platform 6 of Mughalsarai Junction, India.jpg|Upper Class waiting room at Platform 6 of Mughalsarai Junction
चित्र:Platform 7 of Mughalsarai Junction, India.jpg|Platform 7 of Mughalsarai Junction.
चित्र:Platform 4 and 5 of Mughalsarai Junction from flyover.jpg|Platform 4 and 5 of Mughalsarai Junction from flyover
चित्र:Mughalsarai Junction from flyover 01.jpg|A view of Mughalsarai Junction as seen from flyover.
चित्र:Local Train on Platform 5 of Mughalsarai Junction.jpg|Local Train on Platform 5 of Mughalsarai Junction.
चित्र:Green colored coach of 12273 Howrah-New Delhi Duronto Express.jpg|
</gallery>
== इहो देखल जाय ==
* [[बनारस कैंट रेलवे स्टेशन|वाराणसी जंक्शन रेलवे स्टेशन]]
* [[वाराणसी सिटी रेलवे स्टेशन]]
* [[काशी रेलवे स्टेशन]]
* [[बनारस रेलवे स्टेशन]]
* [[गया जंक्शन रेलवे स्टेशन]]
* [[धनबाद जंक्शन रेलवे स्टेशन]]
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:उत्तर प्रदेश में रेलवे स्टेशन]]
12a7uef8s10w2s7qv5jajdj0c9fgnbs
797099
797098
2026-06-08T14:25:15Z
SM7
3953
Protected "[[मुगलसराय जंक्शन रेलवे स्टेशन]]" ([संपादन करीं=Allow only administrators] (indefinite) [स्थानांतरण=Allow only administrators] (indefinite))
797098
wikitext
text/x-wiki
{{Infobox station
| name=Mughalsarai Junction<br />{{small|Pt. Deen Dayal Upadhyaya Junction}}
| native_name_lang=
| style=Indian Railways
| type=[[Express train|Express]] and [[Passenger train|passenger]] station
| image = Deendayalmughalsaraijn.png
| image_size =
| image_caption = पं. दीन दयाल उपाध्याय जंक्शन रेलवे स्टेशन
| address=[[Mughalsarai]] – 232101, [[Uttar Pradesh]]
| country=India
| coordinates={{Coord|25.2767|N|83.1173|E|type:railwaystation_region:IN|format=dms|display=inline,title}}
| elevation={{convert|79.273|m|ft}}
| owned=[[Indian Railways]]
| operator=[[East Central Railway zone|East Central Railways]]
| lines=[[Howrah–Delhi main line]],<br />[[Howrah–Gaya–Delhi line]],<br />[[Howrah–Allahabad–Mumbai line]],<br />[[Gaya–Mughalsarai section]],<br />[[Mughalsarai–Kanpur section]],<br />[[Grand Chord]],<br />[[Patna–Mughalsarai section]],<br />Mughalsarai–Varanasi–Lucknow section
| platforms=8
| tracks=23
| connections=Auto stand
| ADA=
| bicycle=Yes
| parking=Yes
| structure=Standard on ground
| code={{Indian railway code
| code = DDU
| zone = [[East Central Railway zone]]
| division = {{rwd| Mughalsarai}}
}}
status=Functioning
opened={{start date and age|df=yes|1862}}
rebuilt=
electrified=1961–63
passengers=3 lakh passengers per day
pass_system=
pass_year=
pass_percent=
map_type=India Uttar Pradesh
map_dot_label=Mughalsarai Junction
map_caption=Location in [[Uttar Pradesh]]
}}
[[Category:Articles using Infobox station with markup inside name]]
'''मुगलसराय जंक्शन''', आधिकारिक तौर पर '''पं. दीन दयाल उपाध्याय जंक्शन''', (स्टेशन कोड: '''डीडीयू''', पहिले '''एमजीएस''' ) भारत के [[उत्तर प्रदेश]] राज्य के [[मुगलसराय]] शहर में एगो रेलवे स्टेशन बा। <ref>{{Cite news|url=https://www.indiatoday.in/education-today/gk-current-affairs/story/mughalsarai-station-renamed-1306483-2018-08-06|title=After 156 years, Mughalsarai station renamed as Pandit Deen Dayal Upadhyaya Junction: Know all about it|date=6 August 2018|work=[[India Today]]|access-date=17 June 2021|language=en}}</ref> एह स्टेशन पर एशिया के सभसे बड़हन रेलवे मार्शलिंग यार्ड बा। <ref>{{Cite web|url=https://www.irfca.org/faq/faq-yard.html|title=[IRFCA] Indian Railways FAQ: Freight Sheds and Marshalling Yards|publisher=IRFCA|access-date=7 January 2020}}</ref> मुगलसराय यार्ड में एक महीना में लगभग 450–500 ट्रेन के सुविधा मिलेला। <ref>{{Cite news|url=https://www.livemint.com/Politics/JnG2X7OAKevymvTvGEthOK/Railways-to-invest-Rs3000-crore-to-mechanize-automate-yard.html|title=Railways to invest Rs3,000 crore to mechanize, automate yards|last=Sood|first=Jyotika|date=17 October 2017|work=[[Mint (newspaper)|Mint]]|access-date=1 February 2021|language=en}}</ref> प्रीमियम श्रेणी के पूरब ओर जाए वाली राजधानी ट्रेन आ दुरोंतो ट्रेन समेत सगरी ट्रेन रुक जाले (एहसे पूरा भारतीय रेलवे नेटवर्क में एकरा के अनोखा बनावेला; जवन एकरा के प्रयागराज जंक्शन, भोपाल जंक्शन, आगरा कैंट, ग्वालियर जंक्शन, खड़गपुर, नागपुर आदि जइसन अन्य प्रमुख रेलवे स्टेशनन से अलग करेला) एह स्टेशन पर बा। मुगलसराय में प्रमुख इंस्टालेशन में 147 इंजन वाला इलेक्ट्रिक इंजन शेड, 53 इंजन वाला डीजल इंजन शेड, वैगन आरओएच शेड अउरी 169 बेड के डिवीजनल अस्पताल शामिल बा।
== इतिहास ==
ईस्ट इंडियन रेलवे कंपनी दिल्ली आ हावड़ा के जोड़े के काम उन्नीसवीं सदी के मध्य से शुरू कइलस। कराची के लगे (अब पाकिस्तान में) गद्दर के बाद ई दुसरा सभसे बड़ रेलवे स्टेशन रहल जे 1862 में ब्रिटिश शासन के दौरान बनल। परसिद्ध रूप से पूरबी भारत के प्रवेश द्वार के रूप में जानल जाए वाला ई जंक्शन दिल्ली-कलकत्ता मार्ग के जोड़े के प्रोजेक्ट के हिस्सा के रूप में ब्रिटिश रेलवे कंपनी द्वारा बनावल गइल रहे जेकरा के ईस्ट इंडियन रेलवे के नाँव से जानल जाला।
ई स्टेशन ग्रांड ट्रंक रोड मार्ग पर स्थित बा। ई मुगल जमाना के सबसे व्यस्त गलियारा में से एगो रहे जवन पूरबी भारत के उत्तर से जोड़त रहे। 1862 में रेलवे के पटरी मुगलसराय के पार क के [[जमुना|यमुना]] के पच्छिमी किनारे पहुँचल।<ref>{{Cite news|url=https://timesofindia.indiatimes.com/city/varanasi/the-many-names-of-mughalsarai/articleshow/59924547.cms|title=Mughalsarai: The many names of Mughalsarai|last=Dikshit|first=Rajeev|date=5 August 2017|work=[[The Times of India]]|access-date=1 February 2021|language=en}}</ref> दिल्ली के थ्रू लिंक के स्थापना 1866 में भइल <ref>{{Cite web|url=http://www.irfca.org/faq/faq-hist.html|title=IR History: Early History (1832–1869)|publisher=IRFCA|access-date=19 June 2013}}</ref> ग्रांड कॉर्ड के कमीशन 1906 में भइल। <ref>{{Cite web|url=http://www.irfca.org/faq/faq-history3.html|title=IR History: Part III (1900–1947)|publisher=IRFCA|access-date=19 June 2013}}</ref>
[[गंगा नदी|गंडक]] के पार [[राजघाट पुल|डफरिन पुल]] 1887 में खुलल रहे जवन मुगलसराय के [[बनारस कैंट रेलवे स्टेशन|वाराणसी]] से जोड़े ला।<ref>{{Cite web|url=http://www.irfca.org/faq/faq-history2.html|title=IR History: Part II (1870–1899)|publisher=IRFCA|access-date=19 June 2013}}</ref>
== नाँव बदलाव ==
ग्रांड ट्रंक रोड पर स्थापित ई स्टेशन एगो रोचक अतीत के सहेजे ला। शेर शाह सूरी द्वारा बनावल गइल ई सड़क अधिकतर कारवां सभ खातिर मुख्य मारग के काम कइलस, मध्यकालीन दौर में आ बहुत बाद में भी, पूर्वी भा दक्खिन भारत से उत्तर भारत के ओर यात्रा कइलस। जेतना व्यस्त रहे, आ अबहियों बा, सड़क के दुनो ओर कई गो सराय रहली स आ एही से एकर नाम — मुगलसराय पड़ल।
भारतीय जनसंघ के अध्यक्ष चुनला गइला के मुश्किल से दू महीना बाद 10 फरवरी 1968 के सांझ के दीन दयाल उपाध्याय लखनऊ से पटना खातिर सीयालदह एक्सप्रेस में सवार भइलन। कुछ घंटा बाद मुगलसराय स्टेशन प एगो प्लेटफार्म के छोर से कुछ सौ फीट दूर एगो खंभा के लगे उनुकर लाश मिलल।
एकरा बाद जवन भइल उ एगो लंबा अउरी शामिल जांच रहे कि संघ जवना प जोर देले रहे कि उ राजनीतिक मकसद से भइल हत्या ह। सीबीआई के जांच एकरा के दुर्घटना कहलस; दू गो आदमी डकैती के कोशिश में उनुकरा के ट्रेन से बाहर धकेले के कबूल कइले बाकिर सबूत के कमी का चलते बरी कर दिहल गइल; उपाध्याय के शरीर पर कवनो संघर्ष भा चोट के निशान ना रहे। आ संघ में सत्ता के आंतरिक लड़ाई का बारे में षड्यंत्र सिद्धांत आजुओ भरपूर बा। 1992 में [[उत्तर प्रदेश]] राज्य के तत्कालीन सरकार मुगलसराय के नाँव [[दीनदयाल उपाध्याय|दीन दयाल उपाध्याय]] के नाँव पर रखे के कोसिस कइलस हालाँकि, बाबरी महजिद ध्वंस के बाद राज्य में हिंसा के प्रकोप के बाद मुख्यमंत्री कल्याण सिंह के इस्तीफा देवे के पड़ला पर ई योजना ठंडा बस्ता में हो गइल।<ref name="rename">{{Cite news|url=https://www.indiatoday.in/india/story/mughalsarai-station-now-deen-dayal-upadhyay-1305833-2018-08-05|title=Mughalsarai station is now Deen Dayal Upadhyay station|date=5 August 2018|work=[[India Today]]|access-date=21 August 2018}}</ref> 2017 [[भारत सरकार|में भारत सरकार]] [[आदित्यनाथ|योगी आदित्यनाथ]] के नेतृत्व में राज्य सरकार द्वारा भेजल गइल एगो नया प्रस्ताव के मंजूरी दिहलस जवना में स्टेशन के नाम बदल दिहल गइल। <ref>{{Cite news|url=https://indianexpress.com/article/india/here-are-the-railway-stations-which-have-been-renamed-recently-4781759/|title=Mughalsarai railway station renamed after Deen Dayal Upadhyaya: A look at stations that have been renamed recently|date=4 August 2017|work=[[The Indian Express]]|access-date=21 August 2018}}</ref> एह स्टेशन के आधिकारिक रूप से 4 जून 2018 के नाँव बदल के पंडित दीन दयाल उपाध्याय जंक्शन रखल गइल। <ref name="rename" />
== विद्युतीकरण ==
गया–मुगलसराय जंक्शन सेक्टर के विद्युतीकरण 1961-63 में भइल। मुगलसराय यार्ड में 1963-65 में विद्युतीकरण भइल। <ref>{{Cite web|url=http://irfca.org/docs/electrification-history.html|title=History of Electrification|publisher=IRFCA|access-date=19 June 2013}}</ref>
== मार्शलिंग यार्ड ==
मुगलसराय मार्शलिंग यार्ड एशिया के सबसे बड़ बा। <ref name="marshalling">{{Cite web|url=http://www.irfca.org/faq/faq-yard.html|title=Freight Sheds and Mashalling Yards|publisher=IRFCA|access-date=19 June 2013}}</ref> <ref>{{Cite web|url=http://www.ecr.indianrailways.gov.in/uploads/files/1322119784210-Inf.pdf|title=General Information|publisher=East Central Railway|access-date=19 June 2013}}</ref> <ref>{{Cite web|url=http://www.outlookindia.com/article.aspx?210638|title=Mughalsarai: Tracks to Nowhere|publisher=Outlook India, 8 January 2001|access-date=19 June 2013}}</ref> ई 12.5 किमी लंबा बाटे आ डेली लगभग 1,500 वैगन के संभालेला। रेलवे के ओर से टुकड़ा-टुकड़ा लोडिंग बंद होखला के बाद वैगन हैंडलिंग में गिरावट आइल बा। अपना चरम पर ई रोज 5,000 वैगन के संभालत रहे। भारतीय रेलवे पर मौजूद सगरी डिवीजनन में से मुगलसराय डिवीजन में सबसे जादा ट्रेन के संचालन होला – गुड्स आ कोचिंग दुनु के। ई भारत के पूरबी हिस्सा आ उत्तरी हिस्सा के बीच के पुल हवे। ई पिट हेड कोयला आ पावर हाउस, उपयोगकर्ता लोगन खातिर तैयार स्टील उत्पाद, देश के पूर्वी हिस्सा से खाद्य अनाज आ खाद आ उद्योगन के अन्य कच्चा माल के बीच के दूरी कम करे ला। पूर्वी मध्य रेलवे के दक्षता के निर्धारण में डिवीजन के परिचालन दक्षता के अहम भूमिका होला आ एह डिवीजन पर संचालन में कवनो तरह के झटका भा अक्षमता एगो संवेदनशील मामला बा जवन रेलवे के समग्र संचालन के प्रभावित करेला। अपना बहुते महत्व का चलते रेलवे बोर्ड मुगलसराय संभाग के कामकाज पर खास नजर राखेला. <ref name="marshalling" /> <ref>{{Cite web|url=http://indianrailwayemployee.com/node/6016|title=Marshalling Yards|publisher=Indian Railway Employee|access-date=19 June 2013|archive-date=15 January 2013|archive-url=https://web.archive.org/web/20130115212621/http://indianrailwayemployee.com/node/6016|url-status=dead}}</ref>
== शेड आ वर्कशाप ==
मुगल सराय डीजल लोको शेड में WDM-2, WDM-3A आ WDS-5 डीजल लोको के घर बा। डीजल शेड में 50 इलेक्ट्रिक लोको भी बा, जवन कि सभ WAG-7 बा। मुगलसराय में उत्तरी रेलवे के डीजल लोको शेड रहे। एकरा के 2001 में बंद कर दिहल गइल। मुगलसराय इलेक्ट्रिक लोको शेड में 150 से अधिका इलेक्ट्रिक लोको राखल जा सकेलें। एहमें वैप-4 आ 70 से अधिका वैग-7 लोको शामिल बाड़ें। इलेक्ट्रिक शेड में हाल ही में वैग-9 इंजन राखल शुरू हो गइल बा।
भारतीय रेलवे के सबसे बड़ वैगन मरम्मत कार्यशाला मुगलसराय में बा। <ref name="sheds">{{Cite web|url=http://www.irfca.org/faq/faq-shed.html|title=Sheds and workshops|publisher=IRFCA|access-date=19 June 2013}}</ref>
== यात्री लोग के आवाजाही ==
पं. दीन दयाल उपाध्याय जंक्शन भारतीय रेलवे के टॉप सौ बुकिंग स्टेशनन में शामिल बा।<ref>{{Cite web|url=http://www.indianrail.gov.in/7days_Avl.html|title=Indian Railways Passenger Reservation Enquiry|website=Availability in trains for Top 100 Booking Stations of Indian Railways|publisher=IRFCA|archive-url=https://web.archive.org/web/20140510115649/http://www.indianrail.gov.in/7days_Avl.html|archive-date=10 May 2014|access-date=19 June 2013}}</ref>
== सुविधा सभ ==
पं. दीन दयाल उपाध्याय जंक्शन रेलवे स्टेशन में 2 गो एसी कमरा, 4 गो गैर-एसी रिटायरिंग कमरा, आ दस बेड वाला गैर-एसी डोरमेटरी बा। एकरा में फूड प्लाजा आ 'जन आहर' (सस्ती खाना) के सुविधा बा। एह स्टेशन पर राष्ट्रीयकृत बैंकन के एटीएम बा।<ref>{{Cite web|url=http://www.ecr.indianrailways.gov.in/uploads/files/1327473148418-Organizational%20Information-JAN-12.pdf|title=Mughalsarai Division, Commercial Department|publisher=Indian Railways|access-date=19 June 2013}}</ref>
== गैलरी ==
<gallery widths="180">
चित्र:Pt. Deen Dayal Upadhyaya Junction board.jpg|Board of the Pt. Deen Dayal Upadhyaya Junction.
चित्र:Food Track at Mughalsarai Junction platform 6.jpg|Food Track at Mughalsarai Junction platform 6
चित्र:Howrah-New Delhi Duronto Express on Platform 6 of Mughalsarai Junction.jpg|12273 Howrah-New Delhi Duronto Express on Platform 6 of Mughalsarai Junction
चित्र:Upper Class waiting room at Platform 6 of Mughalsarai Junction, India.jpg|Upper Class waiting room at Platform 6 of Mughalsarai Junction
चित्र:Platform 7 of Mughalsarai Junction, India.jpg|Platform 7 of Mughalsarai Junction.
चित्र:Platform 4 and 5 of Mughalsarai Junction from flyover.jpg|Platform 4 and 5 of Mughalsarai Junction from flyover
चित्र:Mughalsarai Junction from flyover 01.jpg|A view of Mughalsarai Junction as seen from flyover.
चित्र:Local Train on Platform 5 of Mughalsarai Junction.jpg|Local Train on Platform 5 of Mughalsarai Junction.
चित्र:Green colored coach of 12273 Howrah-New Delhi Duronto Express.jpg|
</gallery>
== इहो देखल जाय ==
* [[बनारस कैंट रेलवे स्टेशन|वाराणसी जंक्शन रेलवे स्टेशन]]
* [[वाराणसी सिटी रेलवे स्टेशन]]
* [[काशी रेलवे स्टेशन]]
* [[बनारस रेलवे स्टेशन]]
* [[गया जंक्शन रेलवे स्टेशन]]
* [[धनबाद जंक्शन रेलवे स्टेशन]]
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:उत्तर प्रदेश में रेलवे स्टेशन]]
12a7uef8s10w2s7qv5jajdj0c9fgnbs
कुसुमी जंगल
0
88791
797103
774609
2026-06-08T14:46:49Z
SM7
3953
बिस्तार कइल गइल
797103
wikitext
text/x-wiki
{{Infobox forest
| name = कुसुमी जंगल
| native_name =
| photo =
| photo_caption =
| photo_width =
| map = India Uttar Pradesh
| map_caption = उत्तर प्रदेश में कुसुमी जंगल के स्थिति
| map_width =
| coordinates = {{coord|26.749748|83.468645}}
| county =
| region =
| country = [[भारत]]
| elevation =
| area =
| max_area =
| date_max_area =
| status = नेशनल फारेस्ट
| established =
| visitation =
| visitation_year =
| events =
| authority =
| website =
| ecosystem =
| classification_WWF =
| classification_EPA =
| classification_CEC =
| disturbance =
| forest_cover =
| species = साल (शेखुआ)
| indicator_plants =
| lesser_flora =
| fauna =
}}
'''कुसुमी जंगल''' (दूसर नाम: कुसमी, कुसुम्ही, कुष्मी) [[उत्तर प्रदेश]] में [[गोरखपुर]] के पास एक ठो [[जंगल]] बाटे। ई [[राष्ट्रीय बन]] हवे जहाँ साल के पेड़ बाने। ई घना जंगल हवे जेकर मैनेजमेंट आ संरक्षण सरकारी रूप से बन बिभाग के देखरेख में होखे ला। हाल में [[उत्तर प्रदेश सरकार]] एकरा के [[इको-टूरिज्म]] के अस्थान बनावे के घोषणा कइले बाटे। ए जंगल में एगो पार्क बा जे के कुसुमी विनोद वन के नाम से जानल जाएला। एही जंगल में एगो मंदिर बाटे जेकरा के बुढ़िया माई मंदिर के नाँव से जानल जाला।<ref name="News18" /> ई सब जगह गोरखपुर के लोगन खातिर पिकनिक स्पॉट के रूप में बाड़ें आ [[न्यू इयर]] पर बहुत सारा लोग इहँवा जाएला।
== कुसुमी विनोद वन ==
कुसुमी जंगल में एगो पार्क बाटे। सरकारी वेबसाइट के अनुसार ई शहर के रेलवे स्टेशन से 9 किलोमीटर के दूर पर बा आ इहँवा एक गो चिड़ियाघर बाटे जहाँ कुछ जानवर रखल बाने।<ref>{{cite web |title=Places of Interest {{!}} District Gorakhpur {{!}} India |url=https://gorakhpur.nic.in/places-of-interest/ |website=gorakhpur.nic.in |publisher=[[उत्तर प्रदेश सरकार]] |access-date=14 फरवरी 2023}}</ref> कुसुमी जंगल के सरकार इको-टूरिज्म पार्क बनावे के योजना बाना रहल बाटी।<ref>{{cite news |title=गोरखपुर के कुसम्ही जंगल में बनेगा इको टूरिज्म सेंटर, पर्यटकों को करेगा आकर्षित |url=https://www.livehindustan.com/uttar-pradesh/story-eco-tourism-center-will-be-built-in-kusumhi-forest-of-gorakhpur-cm-yogi-appreciated-the-proposal-7046736.html |access-date=14 फरवरी 2023 |work=हिंदुस्तान |date=7 सितंबर 2022 |language=hi}}</ref>
== बुढ़िया माई मंदिर ==
जंगल के बीचा में एगो मंदिर बाटे। एहिजा सालों भर भीड़ रहे ला आ लोग बुढ़िया माई भा बुढ़िया माता के दर्शन करे आवे ला। मान्यता हवे कि इहाँ दर्शन करे से मनौती पूरा होखे ला।<ref name="News18" />
हालाँकि, मंदिर आ बुढ़िया माई के साथे भूतिया कथा किंबदंती जुड़ल बाटे। कहल जाला कि जंगल के बीच नाला से हो के एगो बरात गुजरत रहल। गाँव के एगो बुढ़िया बरात से नाच देखावे के कहलस बाकी बरात के लोग बुढ़िया के मजाक उड़ावल। आगे जा के पूरा बरात नाला के पुल से नाला में गिर गइल, खाली भर एक ठो जोकर जिंदा बचल जे बुढ़िया के आपन नाच देखवले रहल। एही के बाद बुढ़िया माता के मंदिर बनवावल गइल।<ref name="News18">{{cite web |title=यूपी के इस शहर में विराजमान है बुढ़िया माता का मंदिर,जानें मान्यता |url=https://hindi.news18.com/news/uttar-pradesh/gorakhpur-temple-of-budhia-mata-situated-in-the-middle-of-this-forest-know-its-importance-7429907.html |website=News18 हिंदी |access-date=8 जून 2026 |language=hi-IN}}</ref>
== ईहो देखल जाव ==
* [[रामगढ़ ताल]]
* [[गोरखनाथ मंदिर]]
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:गोरखपुर]]
[[श्रेणी:गोरखपुर के पर्यटक अस्थान]]
[[श्रेणी:गोरखपुर जिला]]
{{UP-geo-stub}}
9r97al6h08z1exwilmtqre9rug03953
हनुमान चालीसा
0
89219
797603
796053
2026-06-09T08:56:12Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:भक्ति आंदोलन]] हटावल गइल
797603
wikitext
text/x-wiki
{{Infobox religious text
|name=हनुमान चालीसा
|image=Hanuman showing Rama in His heart.jpg
|author=[[तुलसीदास]]
|religion=[[हिंदू धर्म]]
|language=[[अवधी भाषा]]<ref>Nityanand Misra 2015, p. xviii.</ref>
|Genre=[[भक्ति]] साहित्य
|verses=40
}}
'''हनुमान चालीसा''' चाहे '''हनुमान चलीसा''' [[हिंदू धर्म|हिंदू]] देवता [[हनुमान]] के गुणगान में भक्ति गीत (''[[स्तोत्र]]'') ह।<ref name="mahaviriintro">Rambhadradas 1984, [http://jagadgururambhadracharya.org/works/hcm/amukha.php pp. 1-8.] {{Webarchive|url=https://web.archive.org/web/20140203052448/http://jagadgururambhadracharya.org/works/hcm/amukha.php|date=3 February 2014}}</ref><ref>{{Cite web|url=http://www.thehindubusinessline.com/2003/02/26/stories/2003022601521700.htm|title=Hanuman Chalisa in digital version|date=26 February 2003|publisher=The Hindu Business Line|access-date=2011-06-25}}</ref><ref>{{Cite web|url=https://hindi.news18.com/news/dharm/who-wrote-hanuman-chalisa-the-story-behind-it-2999632.html|title=किसने लिखी थी हनुमान चालीसा, जिसके बारे में कही जाती हैं कई बातें|date=9 April 2020|website=News18 India|access-date=2020-09-15|archive-date=2020-05-04|archive-url=https://web.archive.org/web/20200504125310/https://hindi.news18.com/news/dharm/who-wrote-hanuman-chalisa-the-story-behind-it-2999632.html|url-status=dead}}</ref> [[तुलसीदास]] एकर रचना [[अवधी भाषा]] में कइलें,<ref name="mahaviriintro" /> आ ''[[रामचरितमानस]]'' के अलावा ई इनके सभसे परसिद्ध रचना ह।<ref>{{Cite news|url=http://www.hindu.com/br/2006/01/03/stories/2006010300511400.htm|title=Book Review / Language Books : Epic of Tulasidas|date=3 January 2006|work=The Hindu|access-date=2011-06-25|archive-date=2010-03-04|archive-url=https://web.archive.org/web/20100304141547/http://www.hindu.com/br/2006/01/03/stories/2006010300511400.htm|url-status=dead}}</ref><ref>{{Cite news|url=http://www.hindu.com/thehindu/fr/2002/11/29/stories/2002112900990400.htm|title=Lineage shows|date=29 November 2002|work=[[The Hindu]]|access-date=2011-06-25|archive-url=https://web.archive.org/web/20040103112927/http://www.hindu.com/thehindu/fr/2002/11/29/stories/2002112900990400.htm|archive-date=3 January 2004}}</ref>
हिंदू धार्मिक कथा सभ में हनुमान जी [[राम]] के भक्त हवें आ ''[[रामायण]]'' के केंद्रीय पात्रन में से एक हवें। [[शैव मत|शैव]] परंपरा के अनुसार हनुमानो एगो देवता हवन जे [[शिव]] के अवतार हवें। लोककथा सभ में हनुमान जी के बल-बुद्धि के काफी तारीफ मिले ला।<ref name="peeb100">Peebles 1986, p. 100</ref> हनुमान के गुण — उनकर ताकत, साहस, बुद्धि, [[ब्रह्मचर्य]], राम के प्रति उनकर भक्ति; आ जवना कई गो नाँव सभ से उनकरा के जानल जाला — के बिस्तार से ''हनुमान चालीसा'' में बर्णन कइल गइल बा।<ref name="peeb100" /> ''हनुमान चालीसा'' के पाठ भा जप एगो आम हिंदू धार्मिक रिवाज ह।<ref>Peebles 1986, p. 99</ref> ''हनुमान चालीसा'' हनुमान के गुणगान में सबसे लोकप्रिय गीत ह, आ रोज लाखों हिंदू लोग एकर पाठ करे ला।<ref name="nm2015foreword">[[Karan Singh]], in Nityanand Misra 2015, p. xvi.</ref>
== बिबरन ==
''हनुमान चालीसा'' के अरथ होला — हनुमान पर चालीस ठे चौपाई। "चालीसा" भा "चलीसा" शब्द "चालीस" से बनल बा, जेकर मतलब होला चालीस के संख्या, काहें से कि ''हनुमान चालीसा में'' 40 गो [[चौपाई]] छंद बाड़ें (शुरुआत आ अंत में के दोहा सभ के छोड़ के)।<ref name="mahaviriintro" />
एकर रचना के श्रेय [[तुलसीदास]] के दिहल जाला, जे 16वीं सदी ईसवी में एगो कवि-संत रहलें। स्तोत्र के अंतिम श्लोक में उ आपन नाम के जिकिर कइले बाड़ें। ''हनुमान चालीसा'' के 39वीं चौपाई में कहल गइल बा कि जे [[हनुमान]] जी के पूरा भक्ति से एकर जप करी, ओकरा प हनुमान के कृपा होई। दुनिया भर के हिंदू लोग में ई बहुत लोकप्रिय मान्यता बा कि एह चालीसा के जाप से गंभीर समस्या में हनुमान के दिव्य हस्तक्षेप के आह्वान होला।
=== लेखक ===
[[चित्र:Goswami_Tulsidas_Awadhi_Hindi_Poet.jpg|thumb| [[तुलसीदास]] के सबसे आम तस्वीर]]
[[चित्र:Tulsi_Das_Home_from_the_Ganga_River_near_Hanuman_Ghat,_Varanasi.jpg|thumb| [[गंगा नदी]] के तीरे तुलसीदास के घर तुलसी घाट, [[बनारस]] जहाँ हनुमान चालीसा लिखल गइल रहे, एह स्थल पर एगो छोट मंदिरो बाटे।]]
[[तुलसीदास]]<ref name="frommer" /> (1497/1532-1623) एगो [[हिंदू धर्म|हिंदू]] कवि-संत, सुधारक आ दार्शनिक रहलें जे [[राम]] के प्रति भक्ति खातिर परसिद्ध रहलें। कई गो लोकप्रिय रचना सभ के रचयिता तुलसीदास के सभसे ढेर जानल जाला महाकाव्य ''[[रामचरितमानस]]'' के लेखक के रूप में, जे लोकभाषा में अवधी भाषा में ''[[रामायण]]'' के दोबारा बर्णन हवे। तुलसीदास के उनुका जियते में उनुका के [[संस्कृत]] में मूल रामायण के रचनाकार [[वाल्मीकि]] के पुनर्जन्म के रूप में प्रशंसित कइल गइल रहे।<ref>Lutgendorf 2007, p. 293.</ref> तुलसीदास अपना निधन तक [[बनारस|काशी]] (अब बनारस) में रहत रहलें।<ref>Prasad 2008, p. 857, quoting Mata Prasad Gupta: Although he paid occasional visits to several places of pilgrimage associated with Rama, his permanent residence was in Kashi.</ref> बनारस के तुलसी घाट के नाम उनके नाम पर रखल गइल बा।<ref name="frommer">de Bruyn 2010, p. 471</ref> ऊ बनारस में हनुमान के समर्पित [[संकट मोचन|संकट मोचन हनुमान मंदिर]] के स्थापना कइलें, मानल जाला कि ई ओह जगह पर खड़ा हवे जहाँ इनके हनुमान के साक्षात दर्शन भइल रहल।<ref>Callewaert 2000, p. 90</ref> तुलसीदास [[रामलीला]] नाटकन के शुरुआत कइलन जवन रामायण के लोक-रंगमय रूपांतरण ह।<ref name="handoo-ramlila">Handoo 1964, p. 128: … this book … is also a drama, because Goswami Tulasidasa started his ''Ram Lila'' on the basis of this book, which even now is performed in the same manner everywhere.</ref> [[हिंदी साहित्य|हिंदी]], [[भारतीय साहित्य|भारतीय]], आ विश्व साहित्य के सबसे बड़ कवि में से एक के रूप में उनुकर प्रशंसा मिलल बा।<ref>Prasad 2008, p. xii: He is not only the supreme poet, but the unofficial poet-laureate of India.</ref><ref>Prasad 2008, p. xix: Of Tulsidas's place among the major Indian poets there can be no question: he is as sublime as Valmiki and as elegant as Kalidasa in his handling of the theme.</ref><ref name="jonesryan-tulsi">Jones 2007, p. 456</ref><ref name="nirala-tulsi">Sahni 2000, pp. 78-80</ref> भारत में कला, संस्कृति आ समाज पर तुलसीदास आ इनके रचना सभ के परभाव बहुत ब्यापक बा आ आजु ले लोकभाषा, रामलीला नाटक, हिंदुस्तानी शास्त्रीय संगीत, लोकप्रिय संगीत, आ टेलीविजन धारावाहिक सभ में देखल जाला।<ref name="handoo-ramlila" /><ref>Lutgendorf 1991, p. 11: … — scores of lines from the ''Rāmcaritmānas'' have entered folk speech as proverbs — …</ref><ref>Mitra 2002, p. 216</ref><ref>Subramanian 2008, p. inside cover</ref>
=== भाषा ===
''हनुमान चालीसा'' के 40 गो चौपाई छंद के पहिले शुरू में 2 गो दोहा आ अंत में एगो दोहा बाड़ें।<ref>Mehta 2007, p. xxv</ref> चालीसा में क्रम से ज्ञान, बिना कवनो इच्छा के राम आ मनुष्य के प्रति भक्ति।<ref>Mehta 2007, p. xxvii</ref> जइसे भक्ति साहित्य के मामला में तुलसीदास जी कविता के शुरुआत अपना गुरु (गुरु) के गुणगान करत दू गो दोहा से कइले बाड़न।<ref>Mehta 2007, p. xxxi</ref> चालीसा के भाषा [[अवधी भाषा]] हवे।<ref>Mehta 2007, p. xxxvix</ref>
=== देवता ===
[[हिंदू]] देवता जेकर ई प्रार्थना हवे ऊ [[हनुमान]] हवें, जे [[राम]] ([[विष्णु]] के सातवाँ अवतार) आ ''रामायण'' के एगो केंद्रीय पात्र, के कट्टर भक्त हवें। वानर लोग में सेनापति हनुमान राक्षस राजा [[रावण]] के खिलाफ युद्ध में राम के योद्धा रहले। हनुमान के कारनामा के कई किसिम के धार्मिक आ सांस्कृतिक परंपरा सभ में<ref>Orlando O. Espín, James B. Nickoloff ''An introductory dictionary of theology and religious studies''. 2007, page 537</ref> खासतौर पर हिंदू धर्म में, एह हद ले मनावल जाला कि ऊ अक्सर कुछ भक्ति परंपरा सभ के अनुसार पूजा के बिसय होलें,<ref>Rosen, Steven. ''Essential Hinduism''. 2006, page 67-8</ref> आ कई गो मंदिर सभ में ई प्रधान देवता हवें जे जानल जालें हनुमान मंदिर के रूप में। ऊ सात गो चिरंजीवी (अमर) में से एक हवें। हनुमान जी अर्जुन के रथ पर [[महाभारत]] में भी उनकर ध्वज (झंडा) के रूप में आवेला।
== पाठ ==
एह रचना में तेतालीस गो छंद बाड़ें — दू गो परिचयात्मक दोहा, चालीस गो चौपाई आ अंत में एगो दोहा।[2] पहिला परिचयात्मक दोहा के शुरुआत श्री शब्द से होला जवन शिव के कहल जाला, जेकरा के हनुमान के गुरु मानल जाला।[24] हनुमान के शुभ रूप, ज्ञान, गुण, शक्ति आ बहादुरी के वर्णन पहिला दस चौपाई में कइल गइल बा।[25][26][27] चौपाई एगारह से बीस में हनुमान के राम के सेवा में कइल गइल काम के वर्णन बा आ एगारहवाँ से पन्द्रहवाँ चौपाई में लक्ष्मण के चेतना में वापस ले आवे में हनुमान के भूमिका के वर्णन कइल गइल बा।[25] एकइसवीं चौपाई से तुलसीदास हनुमान के कृपा के जरूरत के वर्णन कइले बाड़न।[28] अंत में तुलसीदास हनुमान जी के सूक्ष्म भक्ति से अभिवादन करे लें[29] आ इनके दिल में आ भक्त लोग के दिल में निवास करे के निहोरा करे लें।[30] समापन दोहा में फिर से हनुमान से राम, लक्ष्मण आ सीता के साथे दिल में निवास करे के निहोरा कइल गइल बा।[31]
== दोहा ==
: ''श्रीगुरु चरण सरोज रज निज मनु मुकुर सुधारि ।
: ''वर्नौ रघुवर विमल जशु जो दायक फल चारि ।।''
: ''बुद्धिहीन तनु जानिके, सुमिरौ पवन कुमार ।
: ''बल बुद्धि विद्या देहु मोहि, हरहु कलेश विकार ।।''
== चौपाई ==
: जय हनुमान ज्ञान गुन सागर ।
: जय कपीस तिहुँ लोक उजागर ॥1॥
: राम दूत अतुलित बल धामा ।
: अंजनि पुत्र पवनसुत नामा ॥2॥
: महाबीर विक्रम बजरंगी ।
: कुमति निवार सुमति के संगी ॥3॥
: कंचन बरन बिराज सुबेसा ।
: कानन कुंडल कुंचित केसा ॥4॥
: हाथ वज्र औ ध्वजा बिराजे ।
: काँधे मूँज जनेऊ साजे ॥5॥
: संकर सुवन केसरी नंदन ।
: तेज प्रताप महा जग वंदन ॥6॥
: विद्यावान गुणी अति चातुर ।
: राम काज करिबे को आतुर ॥7॥
: प्रभु चरित्र सुनिबे को रसिया ।
: राम लखन सीता मनबसिया ॥8॥
: सूक्ष्म रूप धरि सियहिं दिखावा ।
: बिकट रूप धरि लंक जरावा ॥9॥
: भीम रूप धरि असुर सँहारे ।
: रामचंद्र के काज सवाँरे ॥10॥
: लाय सँजीवन लखन जियाए ।
: श्री रघुबीर हरषि उर लाए ॥11॥
: रघुपति कीन्हीं बहुत बड़ाई ।
: तुम मम प्रिय भरतहि सम भाई ॥12॥
: सहस बदन तुम्हरो यस गावै ।
: अस कहि श्रीपति कंठ लगावै ॥13॥
: सनकादिक ब्रह्मादि मुनीसा ।
: नारद सारद सहित अहीसा ॥14॥
: यम कुबेर दिगपाल जहाँ ते ।
: कवि कोविद कहि सके कहाँ ते ॥15॥
: तुम उपकार सुग्रीवहिं कीन्हा ।
: राम मिलाय राज पद दीन्हा ॥16॥
: तुम्हरो मंत्र बिभीषण माना ।
: लंकेश्वर भये सब जग जाना ॥17॥
: जुग सहस्र योजन पर भानू ।
: लील्यो ताहि मधुर फल जानू ॥18॥
: प्रभु मुद्रिका मेलि मुख माहीं ।
: जलधि लाँघि गए अचरज नाहीं ॥19॥
: दुर्गम काज जगत के जेते ।
: सुगम अनुग्रह तुम्हरे तेते ॥20॥
: राम दुआरे तुम रखवारे ।
: होत न आज्ञा बिनु पैसारे ॥21॥
: सब सुख लहैं तुम्हारी सरना ।
: तुम रक्षक काहू को डरना ॥22॥
: आपन तेज सम्हारो आपै ।
: तीनहुँ लोक हाँक ते काँपै ॥23॥
: भूत पिशाच निकट नहि आवै ।
: महाबीर जब नाम सुनावै ॥24॥
: नासै रोग हरे सब पीरा ।
: जपत निरंतर हनुमत बीरा ॥25॥
: संकट तें हनुमान छुडावैं ।
: मन क्रम वचन ध्यान जो लावै ॥26॥
: सब पर राम तपस्वी राजा ।
: तिनके काज सकल तुम साजा ॥27॥
: और मनोरथ जो कोई लावै ।
: सोइ अमित जीवन फल पावै ॥28॥
: चारों जुग परताप तुम्हारा ।
: है परसिद्ध जगत उजियारा ॥29॥
: साधु संत के तुम रखवारे ।
: असुर निकंदन राम दुलारे ॥30॥
: अष्ट सिद्धि नौ निधि के दाता ।
: अस बर दीन जानकी माता ॥31॥
: राम रसायन तुम्हरे पासा ।
: सदा रहो रघुपति के दासा ॥32॥
: तुम्हरे भजन राम को पावै ।
: जनम जनम के दुख बिसरावै ॥33॥
: अंतकाल रघुवरपुर जाई ।
: जहाँ जन्म हरिभक्त कहाई ॥34॥
: और देवता चित्त ना धरई ।
: हनुमत सेई सर्व सुख करई ॥35॥
: संकट कटै मिटै सब पीरा ।
: जो सुमिरै हनुमत बलबीरा ॥36॥
: जै जै जै हनुमान गोसाईं ।
: कृपा करहु गुरु देव की नाईं ॥37॥
: यह सत बार पाठ कर जोई ।
: छूटहि बंदि महा सुख होई ॥38॥
: जो यह पढ़े हनुमान चालीसा ।
: होय सिद्धि साखी गौरीसा ॥39॥
: तुलसीदास सदा हरि चेरा ।
: कीजै नाथ हृदय मँह डेरा ॥40॥
।। दोहा ।।
: ''पवन तनय संकट हरन, मंगल मूरति रूप ।''
: ''राम लखन सीता सहित, हृदय बसहु सुर भूप ॥''
=== टीका सभ ===
1980 के दशक से पहिले ''हनुमान चालीसा'' पर कवनो टीका ना बनावल गइल रहे, जवना के रामभद्राचार्य एह रचना के तुलसीदास के संग्रहित रचना के मुद्रित संस्करण में शामिल ना होखे के कारण बतावेलें।<ref name="mahaviriintro"/> ''हनुमान चालीसा'' पर पहिला संक्षिप्त टीका इंदुभूषण रामायनी के रचना हवे।<ref name="mahaviriintro" /> रामभद्राचार्य के हिंदी में ''महावीरी'' टीका, जेकर रचना 1983 में भइल,<ref name="mahaviriintro" /> रामचंद्र प्रसाद द्वारा ''हनुमान चालीसा'' पर सभसे नीक टीका कहल गइल।
== समीक्षा ==
स्वामी करपात्री ''हनुमान चालीसा के'' वैदिक मंत्रन के तरह एगो परम ''प्रमाण'', सर्वशक्तिमान आ सभ इच्छा के पूरा करे में सक्षम मानत रहले।<ref name="mahaviriintro"/> रामभद्राचार्य एकरा के शुभता से भरल आ "स्तोत्रन के बीच गहना" कहलें, आ कहलें कि ऊ कई गो अइसन उदाहरण के साक्षी आ सुनले बाड़ें जहाँ आस्था के साथ चालीसा पाठ करे वाला लोग के इच्छा पूरा भइल।<ref name="mahaviriintro" />
== पॉपुलर संस्कृति में ==
''हनुमान चालीसा'' के पाठ लाखों हिंदू लोग रोज करे ला<ref name="nm2015foreword">[[Karan Singh]], in Nityanand Misra 2015, p. xvi.</ref> आ भारत के अधिकतर साधक हिंदू लोग के एकर पाठ जबानी इयाद बा।<ref name="nm2015preface">Nityanand Misra 2015, pp. xvii-xxi.</ref> ई रचना बिबिध शैक्षिक, सामाजिक, भाषाई, संगीत, आ भौगोलिक समूह सभ के लोग के बीच लोकप्रिय होखे बा।<ref name="nm2015preface" />
=== शास्त्रीय आ लोक संगीत ===
''हनुमान चालीसा'' हिंदू धार्मिक किताबन में से एगो ह आ एकरा के कई गो लोकप्रिय भजन, शास्त्रीय आ लोक गायक लोग गवले बा।<ref name="nm2015preface">Nityanand Misra 2015, pp. xvii-xxi.</ref> हरि ओम शरण के ''हनुमान चालीसा'' के प्रस्तुति, मूल रूप से 1974 में भारत के ग्रामोफोन कंपनी द्वारा रिलीज कइल गइल आ 1995 में सुपर कैसेट इंडस्ट्रीज द्वारा दोबारा रिलीज कइल गइल,<ref name="nm2015notation" /> सभसे लोकप्रिय सभ में से एक बा, आ नियमित रूप से पूरा उत्तरी भारत के मंदिर आ घर सभ में बजावल जाला।<ref name="nm2015preface" /> ई प्रस्तुति मिश्र खमाज में पारंपरिक धुन पर आधारित बा, [[ठाट|जवन]] खमाज थाट के एगो [[राग]] ह,<ref name="nm2015notation">Nityanand Misra 2015, pp. 199-212.</ref> जवना के आधार स्वर हारमोनियम के दूसरा करिया कुंजी (''काली दो'') पर लिहल गइल बा।<ref name="nm2015notation" /> एही पारंपरिक धुन पर आधारित रिकार्डिंग सुपर कैसेट इंडस्ट्रीज के ओर से 1992 में रिलीज भइल, जवना में हरिहरन गायक आ गुलशन कुमार कलाकार के रूप में रहल लोग।<ref name="nm2015notation" />
अउरी उल्लेखनीय प्रस्तुति में भजन गायक अनुप जलोटा आ रविंद्र जैन, हिंदुस्तानी शास्त्रीय गायक पंडित जसराज आ राजन आ साजन मिश्रा, आ कर्नाटक गायिका एमएस सुब्बुलक्ष्मी के प्रस्तुति शामिल बा।<ref name="nm2015notation">Nityanand Misra 2015, pp. 199-212.</ref> उन्नी कृष्णन, नित्यश्री महादेवन, पंडित भीमसेन जोशी, गणपति सच्चिदानन्द स्वामीजी आ मोररी बापू के प्रस्तुति भी लोकप्रिय बा।
पाश्चात्य गायकन में कृष्ण दास हनुमान चालीसा के धीमा आ तेज दुनु प्रारूप में प्रस्तुत कइले बाड़न.<ref>{{Cite web|url=https://krishnadas.com/podcasts/call-response/spiritual-experiences-auschwitz-and-bernie-glassman/|title=Ep. 27 | Spiritual Experiences, Auschwitz and Bernie Glassman|date=June 15, 2020}}</ref>
=== पापुलर फिलिम ===
हिंदी सिनेमा ''1920'' (निर्देशक विक्रम भट्ट) में ''हनुमान चालीसा के'' अक्सर अलग-अलग सीन में इस्तेमाल भइल। एगो सीन में नायक अर्जुन सिंह राठोड (रजनीश दुग्गल के भूमिका में), ''हनुमान चालीसा'' के पूरा पाठ करत देखावल गइल बा। एकर प्रयोग ''बजरंगी भाईजान में एगो महत्वपूर्ण सीक्वेंस में भइल बा,'' जब नायक बाल तस्करन से जवाबी लड़ाई लड़त बा आ ओह लोग से एगो छोट लड़िकी के बचा लेला।<ref>{{Cite web|url=https://timesofindia.indiatimes.com/bajrangi-bhaijaan-plot-summary/articleshow/48108672.cms|title=Bajrangi Bhaijaan Plot Summary – Times of India|website=The Times of India|language=en|access-date=2021-02-01}}</ref>
चारुवी अग्रवाल के निर्देशन आ चारुवी डिजाइन लैब्स के डिजाइन कइल ''श्री हनुमान चालीसा'' नाम के एगो एनीमेशन फिलिम हनुमान पर बनल फिलिम ह।<ref>{{Cite web|url=http://animationgalaxy.in/Newsindetail.aspx?Dept=Creatives&PID=96|title=Charuvi Design Labs release The Second official teaser for "Shri Hanuman Chalisa"|archive-url=https://web.archive.org/web/20160423025521/http://animationgalaxy.in/Newsindetail.aspx?Dept=Creatives&PID=96|archive-date=23 April 2016|access-date=2016-04-06}}</ref><ref>{{Cite web|url=http://animationgalaxy.in/Newsindetail.aspx?Dept=Creatives&PID=91|title=Charuvi Design Labs release The first official teaser for "Shri Hanuman Chalisa"|archive-url=https://web.archive.org/web/20160423033637/http://animationgalaxy.in/Newsindetail.aspx?Dept=Creatives&PID=91|archive-date=23 April 2016|access-date=2016-04-06}}</ref>
=== पापुलर संगीत ===
''हनुमान चालीसा'' गावल लोकप्रिय गायकन में कर्नाटक गायक एमएस सुब्बुलक्ष्मी के साथे [[लता मंगेशकर]], महेंद्र कपूर, एसपी बालासुब्रह्मण्यम, शंकर महादेवन, अनुराधा पौडवाल, कैलाश खेर, सुखविंदर सिंह, आ [[उदित नारायण]] शामिल बाड़े।<ref name="nm2015preface">Nityanand Misra 2015, pp. xvii-xxi.</ref>
''हनुमान चालीसा'' [[अमिताभ बच्चन]] बीस गो अउरी गायकन का साथे कोरस में गवले रहले।<ref name="nm2015preface">Nityanand Misra 2015, pp. xvii-xxi.</ref> ई रिकार्डिंग ''श्री हनुमान चालीसा'' एल्बम के हिस्सा के रूप में 2011 में रिलीज भइल आ नवंबर 2011 के दौरान रिलीजिंग म्यूजिक लेबल द्वारा एकरा के अभूतपूर्व प्रतिक्रिया मिलल<ref>{{Cite news|url=http://articles.timesofindia.indiatimes.com/2011-11-06/news-and-interviews/30364066_1_tv-ad-campaign-music-label-album|title=All in praise of the Almighty|date=6 November 2011|work=[[The Times of India]]|access-date=10 June 2012|archive-url=https://web.archive.org/web/20111109075136/http://articles.timesofindia.indiatimes.com/2011-11-06/news-and-interviews/30364066_1_tv-ad-campaign-music-label-album|archive-date=9 November 2011}}</ref>
गुलशन कुमार आ हरिहरन के गावल ''हनुमान चालीसा'' के एगो प्रस्तुति नवंबर 2021 में पहिला भक्ति गीत आ [[यूट्यूब]] पर पहिला बेर बनल जवन 2 अरब व्यूज पार कइलस। वर्तमान में [[यूट्यूब]] पर भी इ सबसे ज्यादा देखल जाए वाला भारतीय म्यूजिक वीडियो बा।<ref>{{Cite web|url=https://infotonline.com/hanuman-chalisa-by-gulshan-kumar-crosses-1b-views/|title=Hanuman Chalisa by Gulshan Kumar crosses 1B views on YouTube, another World record made by T-series|date=27 May 2020|website=Infotonline|access-date=May 27, 2020|archive-date=20 September 2020|archive-url=https://web.archive.org/web/20200920180754/https://infotonline.com/hanuman-chalisa-by-gulshan-kumar-crosses-1b-views/|url-status=dead}}</ref>
== संदर्भ ==
{{Reflist|33em}}
=== Bibliography ===
{{refbegin|30em}}
* {{cite book |last1=de Bruyn |first1=Pippa |last2=Bain |first2=Keith |last3=Allardice |first3=David |last4=Joshi |first4=Shonar |title=Frommer's India |year=2010 |publisher=John Wiley and Sons |isbn=978-0-470-60264-5 |location=Hoboken, New Jersey |page=471}}
* {{cite book |last1=Callewaert |first1=Winand M. |last2=Schilder |first2=Robert |title=Banaras: Vision of a Living Ancient Tradition |year=2000 |publisher=Hemkunt Press |isbn=9788170103028 |location=New Delhi, India |page=90}}
* {{cite book |last=Chaturvedi |first=B.K. |title=Shri Hanuman Chalisa (Roman) |year=1994b |publisher=Diamond Pocket Books |location=New Delhi |isbn=81-7182-395-5}}
* {{cite book |last1=Jones |first1=Constance |last2=Ryan |first2=James D. |isbn=978-0-8160-5458-9 |title=Encyclopedia of Hinduism |series=Encyclopedia of World Religions |location=New York |year=2007 |publisher=Infobase Publishing |page=456 |quote=It can be said without reservation that Tulsidas is the greatest poet to write in the Hindi language. Tulsidas was a Brahmin by birth and was believed to be a reincarnation of the author of the Sanskrit Ramayana, Valmiki.}}
* {{cite book |last=Mehta |first=Pt. Vijay Shankar |title=Kripa Karahu Guru Dev Ki Naain |year=2007 |publisher=Radhakrishnan Prakashan |location=New Delhi |isbn=978-81-8361-041-4 |page=9 |edition=2nd}}
* {{cite book |last=Misra |first=Munindra |title=Shri Hanuman Chalisa in English Rhyme with original text |year=2015 |publisher=Osmora Inc. |location=United States |isbn=9782765913702}}
* {{cite book |last=Misra |first=Nityanand |author-link=Nityanand Misra |title=Mahāvīrī: Hanumān-Cālīsā Demystified |year=2015 |publisher=Niraamaya Publishing Services Pvt Ltd |location=Mumbai, India |isbn=9788193114407}}
* {{cite book |last=Mitra |first=Swati |title=Good Earth Varanasi City Guide |publisher=Eicher Goodearth Limited |location=New Delhi, India |year=2002 |isbn=9788187780045 |page=216}}
* {{cite book |last=Peebles |first=Patrick |title=Voices of South Asia: Essential Readings from Antiquity to the Present |publisher=M.E. Sharpe Inc. |location=United States |year=1986 |isbn=978-0-7656-3480-1 |page=216}}
* {{cite web |last=Rambhadradas |author-link=Rambhadracharya |website=Jagadgururambhadracharya.org |date=8 June 1984 |publisher=Krishnadas Charitable Trust |location=New Delhi, India |language=hi |trans-title=Shri Hanuman Chalisa (with the Mahaviri commentary) |title= |script-title=hi:संकट तें हनुमान छुड़ावै। मन क्रम बचन ध्यान जो लावै |url=http://jagadgururambhadracharya.org/works/hcm/contents.php |access-date=29 May 2013 |archive-url=https://web.archive.org/web/20131117205618/http://jagadgururambhadracharya.org/works/hcm/contents.php |archive-date=17 November 2013}}
* {{cite book |last=Rao |first=Cheeni |title=In Hanuman's Hands: A Memoir |year=2009 |publisher=Harper Collins Publishers |location=United States |isbn=978-0-06-073662-0 |page=393 |edition=First}}
* {{cite book |last=Sahni |first=Bhisham |author-link=Bhisham Sahni |isbn=9788171789603 |title=Nilu, Nilima, Nilofara |location=New Delhi, India |language=hi |year=2000 |publisher=Rajkamal Prakashan Pvt Ltd |pages=78–80 |quote=हिन्दी का सौभाग्य है कि उसके काव्यकुंज की तुलसी-मंजरी की जैसी सुगंध संसार की साहित्य वाटिका में शायद कहीं नहीं। ... आकर्षण दोनों में अत्यधिक है अपने-अपने ढंग पर दोनों ही बहुत बड़े हैं, पर फिर भी सब तरफ़ से केवल काव्य के सौंदर्य पर विचार करने पर तुलसीदास ही बड़े ठहरते हैं – भाषा साहित्य में रवीन्द्रनाथ के संबंध में कहना पड़ता है कि भ्रम त्रुटियाँ मिल सकती हैं पर तुलसीदास के संबंध में कोई शायद ही मिले। ... और यही कारण है निराला जी तुलसीदास को कालिदास, व्यास, वाल्मीकि, होमर, गेटे और शेक्सपियर के समकक्ष रखकर उनके महत्त्व का आकलन करते हैं।}}
* {{cite book |last=Subramanian |first=Vadakaymadam Krishnier |location=New Delhi, India |title=Hymns of Tulsidas |publisher=Abhinav Publications |year=2008 |isbn=9788170174967 |page=inside cover |quote=Famous classical singers like Paluskar, Anoop Jalota and MS Subbulakshmi have popularised Tulsidas's hymns among the people of India.}}
{{refend}}
[[श्रेणी:हिंदी कबितई]]
[[श्रेणी:हिंदी भक्ति गीत]]
[[श्रेणी:हिंदू भक्ति गीत]]
5bptpmtai23dkpc1cot2pm8pa4l1plq
ट्रिपल सी
0
92823
797097
796306
2026-06-08T14:19:35Z
SM7
3953
सफाई कइल गइल
797097
wikitext
text/x-wiki
'''ट्रिपल सी''' ({{Lang|en|CCC}}), पूरा नाँव '''कोर्स ऑन कंप्यूटर कॉन्सेप्ट्स''' (Course on Computer Concepts) एगो [[कंप्यूटर]] कोर्स हवे जे भारत में 'राष्ट्रीय इलेक्ट्रॉनिकी आ इन्फार्मेशन टेक्नालॉजी इंस्टिट्यूट' (NIELIT) द्वारा चलावल जाला। ई कोर्स 2 हप्ता के होला आ आम लोगन खाती [[इन्फॉर्मेशन टेक्नोलॉजी]] (IT) के जानकारी देवे खातिर डिजाइन कइल गइल हवे। एह तरीका के आम जानकारी के अलावा एकर उपयोग कंपटीशन एक्जाम सभ में भी बाटे आ कई अइसन नोकरी वाला परीक्षा बाड़ी स जिनहन में CCC परीक्षा पास होखल माँगल जाला।
कोर्स ऑफलाइन आ ऑनलाइन दुनों तरीका से उपलब्ध बाटे आ हर महीना एकर परीक्षा करावल जाला जेह में परीक्षार्थी के 90 मिनट में 100 गो सवालन के जबाब देवे के होला।
== आउटकम ==
कोर्स पूरा कइला के बाद कंप्यूटर के इस्तेमाल से अपना सर्विस/बिजनेस में चिट्ठी-पतरी तइयार करे, [[इंटरनेट]] ([[वेबसाइट|वेब]]) पर जानकारी देखे, [[ई मेल|ईमेल]] पावे आ भेजे, आपन बिजनेस प्रेजेंटेशन तइयार करे, छोट डाटाबेस तइयार करे नियर चीजन के बेसिक मकसद पूरा करे में सक्षम हो सके ला। ई छोट बिजनेस कम्युनिटी, गृहिणी नियन लोगन के कंप्यूटर के इस्तेमाल से आपन छोट खाता बनावे में मदद करेला आ [[इनफार्मेशन टेक्नोलॉजी]] के दुनिया के आनंद उठावे में सक्षम बनावे ला। एही से ई कोर्स थियरी की बजाय प्रेटीकल पर बेसी जोर दे के बनावल गइल हवे।
== बाहरी कड़ी ==
* [https://student.nielit.gov.in/ NIELIT के इस्टूडेंट पोर्टल], जहाँ कोर्स के जानकारी हासिल कइल जा सके ला आ रजिस्ट्रेशन करावल जा सके ला।
[[श्रेणी:कंप्यूटर कोर्स]]
[[श्रेणी:इन्फॉर्मेशन टेक्नोलॉजी]]
{{edu-stub}}
{{comp-sci-stub}}
7gqocee7eok7eg4evmgntoh14a4bz45
797104
797097
2026-06-08T14:53:11Z
SM7
3953
+विकिकड़ी जोड़ल गइल, बाहरी कड़ी जोड़ल गइल
797104
wikitext
text/x-wiki
'''ट्रिपल सी''' ({{Lang|en|CCC}}), पूरा नाँव '''कोर्स ऑन कंप्यूटर कॉन्सेप्ट्स''' (Course on Computer Concepts) एगो [[कंप्यूटर]] कोर्स हवे जे भारत में 'राष्ट्रीय इलेक्ट्रॉनिकी आ इन्फार्मेशन टेक्नालॉजी इंस्टिट्यूट' (NIELIT) द्वारा चलावल जाला। ई कोर्स 2 हप्ता के होला आ आम लोगन खाती [[इन्फॉर्मेशन टेक्नोलॉजी]] (IT) के जानकारी देवे खातिर डिजाइन कइल गइल हवे। एह तरीका के आम जानकारी के अलावा एकर उपयोग कंपटीशन एक्जाम सभ में भी बाटे आ कई अइसन नोकरी वाला परीक्षा बाड़ी स जिनहन में CCC परीक्षा पास होखल माँगल जाला।
कोर्स ऑफलाइन आ ऑनलाइन ([[बीडियो लेक्चर]] के माध्यम से) दुनों तरीका से उपलब्ध बाटे आ हर महीना एकर परीक्षा करावल जाला जेह में परीक्षार्थी के 90 मिनट में 100 गो सवालन के जबाब देवे के होला।
== आउटकम ==
कोर्स पूरा कइला के बाद कंप्यूटर के इस्तेमाल से अपना सर्विस/बिजनेस में चिट्ठी-पतरी तइयार करे, [[इंटरनेट]] ([[वेबसाइट|वेब]]) पर जानकारी देखे, [[ई मेल|ईमेल]] पावे आ भेजे, आपन बिजनेस प्रेजेंटेशन तइयार करे, छोट डाटाबेस तइयार करे नियर चीजन के बेसिक मकसद पूरा करे में सक्षम हो सके ला। ई छोट बिजनेस कम्युनिटी, गृहिणी नियन लोगन के कंप्यूटर के इस्तेमाल से आपन छोट खाता बनावे में मदद करेला आ [[इनफार्मेशन टेक्नोलॉजी]] के दुनिया के आनंद उठावे में सक्षम बनावे ला। एही से ई कोर्स थियरी की बजाय प्रेटीकल पर बेसी जोर दे के बनावल गइल हवे।
== बाहरी कड़ी ==
* [https://student.nielit.gov.in/ NIELIT के इस्टूडेंट पोर्टल], जहाँ कोर्स के जानकारी हासिल कइल जा सके ला आ रजिस्ट्रेशन करावल जा सके ला।
* [https://lms.nielit.gov.in/course/index.php?categoryid=2 NIELIT के पोर्टल पर CCC के पन्ना], पूरा कोर्स देखल जा सके ला [[बीडियो लेक्चर]] देखल जा सके लें।
[[श्रेणी:कंप्यूटर कोर्स]]
[[श्रेणी:इन्फॉर्मेशन टेक्नोलॉजी]]
{{edu-stub}}
{{comp-sci-stub}}
0syj7kqmxy8saoethjclooxthntxgig
प्रयोगकर्ता वार्ता:Shiv Shilpi
3
93336
797096
765477
2026-06-08T13:11:02Z
Shiv Shilpi
34150
/* */ जबाब
797096
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="display:inline-block;margin-top:.1em; text-align:right; margin-bottom:.2em; border-bottom:0; font-weight:bold;"><big>Welcome! स्वागतम्!</big> [[File:Crystal Clear app ksmiletris.png|25px]] </div>
राउर बहुत-बहुत स्वागत बा '''{{BASEPAGENAME}}''' जी ! {{#if: | {{{1}}} | }}<br/>
<div style="float:right; <!--background:#F5F5DC;--> width:30%">
<!-- दाहिना साइडबार -->
{| border="5" cellspacing="10" cellpadding="5" height="50" align=center border=0 style="background: transparent"
|-
| <big>'''ई जरूर पढ़ल जाय:'''</big>
|-
| 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;" | [[विकिपीडिया:समुदाय पोर्टल|सदस्य समाज पन्ना]]
|}
|}
-- [[प्रयोगकर्ता:नया सदस्य स्वागतकर्ता|नया सदस्य स्वागतकर्ता]] ([[प्रयोगकर्ता वार्ता:नया सदस्य स्वागतकर्ता|बात करीं]]) 19:08, 16 नवंबर 2023 (UTC)
:बहुत बहुत धन्यवाद sir जी जानकारी देवे खातिर [[प्रयोगकर्ता:Shiv Shilpi|Shiv Shilpi]] ([[प्रयोगकर्ता वार्ता:Shiv Shilpi|बात करीं]]) 13:11, 8 जून 2026 (UTC)
tr1lyjb4h5jn00m5ls2j8x9z90obar4
विकिपीडिया:आँकड़ा सभ/२०२६/जून
4
100967
797106
796877
2026-06-08T19:21:32Z
NeechalBOT
7874
statistics
797106
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 = 81762
|dPages = 21
|Articles = 9065
|dArticles = 6
|Edits = 793428
|dEdits = 62
|Files = 54
|dFiles = 0
|Users = 40031
|dUsers = 9
|Ausers = 55
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =६-६-२०२६
|Pages = 81791
|dPages = 29
|Articles = 9073
|dArticles = 8
|Edits = 793679
|dEdits = 251
|Files = 54
|dFiles = 0
|Users = 40034
|dUsers = 3
|Ausers = 55
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =८-६-२०२६
|Pages = 81811
|dPages = 20
|Articles = 9078
|dArticles = 5
|Edits = 793919
|dEdits = 240
|Files = 54
|dFiles = 0
|Users = 40046
|dUsers = 12
|Ausers = 55
|dAusers = 0
}}
<!---Place new stats here--->
|}
<!--- stats ends--->
m6iya3rz9ryooy3pkyirsm37b6cta5p
भक्ति आंदोलन
0
101004
797105
796935
2026-06-08T19:21:19Z
SM7
3953
बिस्तार कइल गइल
797105
wikitext
text/x-wiki
[[File:Meerabai (crop).jpg|thumb|[[कृष्ण]] भक्ती आंदोलन के मुख्य हिस्सा रहल बाड़ें। इनके एगो प्रमुख भक्त [[मीराबाई]] रहली (फोटो में देखावल)<ref name="smpandey">{{cite journal|author= SM Pandey |date= 1965 |title= Mīrābāī and Her Contributions to the Bhakti Movement |journal= History of Religions |volume= 5 |number= 1 |pages= 54–73|jstor= 1061803 |doi= 10.1086/462514 |s2cid= 162398500 }}</ref>]]
'''भक्ति आंदोलन''' मध्यकालीन [[हिंदू धर्म]] के एगो महत्त्वपूर्ण धार्मिक आंदोलन रहे,{{sfnp|Schomer|McLeod|1987|p=1}} जेकर मकसद [[भक्ति]] के माध्यम से [[मोक्ष]] (मुक्ती) प्राप्ति के मार्ग अपना के<ref name="cbseindiatoday">{{Cite web|url=https://www.indiatoday.in/education-today/gk-current-affairs/story/-crashcourse-cbse-class-12-history-bhakti-movement-s-emergence-and-influence-1438286-2019-01-24|title=CBSE Class 12 History #CrashCourse: Bhakti movement's emergence and influence|last1=India Today Web Desk New|date=January 24, 2019|website=India Today}}</ref> समाज के सभ वर्गन तक धार्मिक सुधार पहुँचावल रहे। एह आंदोलन के शुरुआत 6वीं सदी ईस्वी में{{sfn|Hawley|2015|p=87}}<ref>{{Cite book |last=Padmaja |first=T. |url=https://books.google.com/books?id=pzgaS1wRnl8C&dq=bhakti+movement+tamilakam&pg=RA1-PA37 |title=Temples of Kr̥ṣṇa in South India: History, Art, and Traditions in Tamil nāḍu |date=2002 |publisher=Abhinav |isbn=978-81-7017-398-4 |language=en}}</ref> [[तमिलकम]] (प्राचीन तमिल क्षेत्र) में भइल मानल जाला। दक्खिन भारत में शुरुआती मध्यकाल के दौरान वैष्णव आलवार आ शैव नायनार संत लोग के भक्ति-प्रधान कविता आ उपदेशन के माध्यम से एह आंदोलन के बिसेस पहिचान मिलल।{{sfnp|Schomer|McLeod|1987|p=1}} बाद में ई आंदोलन धीरे-धीरे उत्तर दिशा में फइलल आ भारत के बाकी अलग-अलग क्षेत्रन तक पहुँच गइल। 15वीं सदी के बाद भक्ति आंदोलन पूरबी आ उत्तरी भारत में तेजी से फइलल आ 15वीं से 17वीं सदी ईस्वी के बीच अपना चरम पर पहुँच गइल। ई आंदोलन धार्मिक आ सामाजिक जीवन पर गम्हीर प्रभाव डललस आ भक्ति के व्यक्तिगत आ सहज मार्ग के लोकप्रिय बनवलस।{{sfnp|Schomer|McLeod|1987|pp=1-2}}
भक्ति आंदोलन भारतीय उपमहादीप के अलग-अलग क्षेत्रन में अलग-अलग हिंदू देवी-देवतन के केंद्र में रख के बिकसित भइल। एह आंदोलन के कुछ प्रमुख धारा सभ में [[वैष्णव संप्रदाय|वैष्णव मत]] ([[विष्णु]] के उपासना), [[शैव मत]] ([[शिव]] के उपासना), [[शाक्त संप्रदाय|शाक्त मत]] ([[शक्ति (देवी)|शक्ति]] देवी के उपासना) आ [[स्मार्त|स्मार्त परंपरा]] शामिल रहल।<ref>{{cite book |first=Lance |last=Nelson |year=2007 |title=An Introductory Dictionary of Theology and Religious Studies |editor1-first=Orlando O. |editor1-last=Espín |editor2-first=James B. |editor2-last=Nickoloff |publisher=Liturgical Press |isbn=978-0814658567 |pages=562–563}}</ref><ref>{{cite book |first=S. S. |last=Kumar |year=2010 |title=Bhakti – the Yoga of Love |publisher=LIT |location=Münster |isbn=978-3643501301 |pages=35–36}}</ref><ref name="donigerbrit">{{cite encyclopedia |first=Wendy |last=Doniger |year=2009 |url=http://www.britannica.com/EBchecked/topic/63933/bhakti |title=Bhakti |encyclopedia=Encyclopædia Britannica}}</ref><ref name="Surinder 1999">{{cite book|last1=Johar|first1=Surinder|title=Guru Gobind Singh: A Multi-faceted Personality|date=1999|publisher=MD Publications|isbn=978-8-175-33093-1|page=89}}</ref> भक्ति आंदोलन के संत आ प्रचारक लोग अपना उपदेश आ रचना सभ खातिर लोकल भाषा-बोली सभ के इस्तेमाल कइल लोग, जवना से कि ओह लोगन के संदेश आम जनता तक आसानी से पहुँच सके। एह आंदोलन के प्रेरणा अनेक कवि-संतन से मिलल, जिनकर विचारधारा बहुत विविध किसिम के रहल। एहमें द्वैत दर्शन के ईश्वरवादी मत से लेके [[अद्वैत वेदांत]] के पूर्ण अद्वैतवादी दर्शन तक के विचार शामिल रहल।{{sfnp|Schomer|McLeod|1987|p=2}}<ref name=novetzke>{{cite journal|author= Christian Novetzke |date= 2007 |title= Bhakti and Its Public |journal= International Journal of Hindu Studies |volume= 11 |number= 3 |pages= 255–272|jstor= 25691067 |doi= 10.1007/s11407-008-9049-9 |s2cid= 144065168 }}</ref> ई सगरी संत लोग भक्ति के माध्यम से आध्यात्मिक उन्नति के मार्ग बतावल लोग।
परंपरागत रूप से भक्ति आंदोलन के हिंदू धर्म के एगो प्रभावशाली सामाजिक सुधार आंदोलन मानल गइल बा, काहें से कि ई जनम, जाति भा लिंग से ऊपर उठ के हर ब्यक्ती खातिर आध्यात्मिकता के आपन निजी मार्ग उपलब्ध करावे ला।{sfnp|Schomer|McLeod|1987|pp=1–2}} हालाँकि, कुछ आधुनिक बिद्वान लोग एह बात पर सवाल उठावेलन कि भक्ति आंदोलन वास्तव में सामाजिक सुधार भा विद्रोह के रूप में उभरल रहे कि ना। एह लोगन के हिसाब से, भक्ति आंदोलन के प्राचीन वैदिक परंपरा के दुबारा जिंदा होखे (रिवाइवल), पुनर्व्याख्या (रिवार्किंग) आ नया सामाजिक संदर्भ में पुनर्स्थापन (रीकॉन्टेक्स्चुअलाइजेशन) के रूप में समझल-मानल जा सकेला।{{sfnp|Pechilis Prentiss|2014|pages= 15-16}}
{{clear}}
== इहो देखल जाय ==
* [[सूफीवाद]]
== टीका-टिप्पणी ==
{{reflist|group=note}}
{{notelist}}
== फुटनोट आ संदर्भ ==
{{Reflist|29em}}
== किताबी स्रोत आ ग्रंथ सूची ==
{{refbegin|29em}}
{{refend}}
== बाहरी कड़ी ==
{{Commons category|Bhakti movement}}
{{Wikiquote|Bhakti movement}}
* [http://sites.fas.harvard.edu/~fc12/Bibliography/09_Bhakti_Bibliography.html Bhakti bibliography] {{Webarchive|url=https://web.archive.org/web/20160304123833/http://sites.fas.harvard.edu/~fc12/Bibliography/09_Bhakti_Bibliography.html |date=4 March 2016 }}, Harvard University Archive (2001)
* [[Wikisource: The Complete Works of Swami Vivekananda/Volume 3/Bhakti-Yoga/Definition of Bhakti|Definition of Bhakti]], Swami Vivekananda, Wikisource
{{हिंदू धर्म बिसय}}
[[श्रेणी:भक्ति आंदोलन| ]]
[[श्रेणी:हिंदू धर्म के इतिहास]]
[[श्रेणी:हिंदू धर्म]]
[[श्रेणी:सामाजिक आंदोलन]]
{{hinduism-stub}}
bqcjiiys07kohoo7knefeq2kj0a5o7s
797107
797105
2026-06-08T19:21:48Z
SM7
3953
सुधार कइल गइल
797107
wikitext
text/x-wiki
[[File:Meerabai (crop).jpg|thumb|[[कृष्ण]] भक्ती आंदोलन के मुख्य हिस्सा रहल बाड़ें। इनके एगो प्रमुख भक्त [[मीराबाई]] रहली (फोटो में देखावल)<ref name="smpandey">{{cite journal|author= SM Pandey |date= 1965 |title= Mīrābāī and Her Contributions to the Bhakti Movement |journal= History of Religions |volume= 5 |number= 1 |pages= 54–73|jstor= 1061803 |doi= 10.1086/462514 |s2cid= 162398500 }}</ref>]]
'''भक्ति आंदोलन''' मध्यकालीन [[हिंदू धर्म]] के एगो महत्त्वपूर्ण धार्मिक आंदोलन रहे,{{sfnp|Schomer|McLeod|1987|p=1}} जेकर मकसद [[भक्ति]] के माध्यम से [[मोक्ष]] (मुक्ती) प्राप्ति के मार्ग अपना के<ref name="cbseindiatoday">{{Cite web|url=https://www.indiatoday.in/education-today/gk-current-affairs/story/-crashcourse-cbse-class-12-history-bhakti-movement-s-emergence-and-influence-1438286-2019-01-24|title=CBSE Class 12 History #CrashCourse: Bhakti movement's emergence and influence|last1=India Today Web Desk New|date=January 24, 2019|website=India Today}}</ref> समाज के सभ वर्गन तक धार्मिक सुधार पहुँचावल रहे। एह आंदोलन के शुरुआत 6वीं सदी ईस्वी में{{sfn|Hawley|2015|p=87}}<ref>{{Cite book |last=Padmaja |first=T. |url=https://books.google.com/books?id=pzgaS1wRnl8C&dq=bhakti+movement+tamilakam&pg=RA1-PA37 |title=Temples of Kr̥ṣṇa in South India: History, Art, and Traditions in Tamil nāḍu |date=2002 |publisher=Abhinav |isbn=978-81-7017-398-4 |language=en}}</ref> [[तमिलकम]] (प्राचीन तमिल क्षेत्र) में भइल मानल जाला। दक्खिन भारत में शुरुआती मध्यकाल के दौरान वैष्णव आलवार आ शैव नायनार संत लोग के भक्ति-प्रधान कविता आ उपदेशन के माध्यम से एह आंदोलन के बिसेस पहिचान मिलल।{{sfnp|Schomer|McLeod|1987|p=1}} बाद में ई आंदोलन धीरे-धीरे उत्तर दिशा में फइलल आ भारत के बाकी अलग-अलग क्षेत्रन तक पहुँच गइल। 15वीं सदी के बाद भक्ति आंदोलन पूरबी आ उत्तरी भारत में तेजी से फइलल आ 15वीं से 17वीं सदी ईस्वी के बीच अपना चरम पर पहुँच गइल। ई आंदोलन धार्मिक आ सामाजिक जीवन पर गम्हीर प्रभाव डललस आ भक्ति के व्यक्तिगत आ सहज मार्ग के लोकप्रिय बनवलस।{{sfnp|Schomer|McLeod|1987|pp=1-2}}
भक्ति आंदोलन भारतीय उपमहादीप के अलग-अलग क्षेत्रन में अलग-अलग हिंदू देवी-देवतन के केंद्र में रख के बिकसित भइल। एह आंदोलन के कुछ प्रमुख धारा सभ में [[वैष्णव संप्रदाय|वैष्णव मत]] ([[विष्णु]] के उपासना), [[शैव मत]] ([[शिव]] के उपासना), [[शाक्त संप्रदाय|शाक्त मत]] ([[शक्ति (देवी)|शक्ति]] देवी के उपासना) आ [[स्मार्त|स्मार्त परंपरा]] शामिल रहल।<ref>{{cite book |first=Lance |last=Nelson |year=2007 |title=An Introductory Dictionary of Theology and Religious Studies |editor1-first=Orlando O. |editor1-last=Espín |editor2-first=James B. |editor2-last=Nickoloff |publisher=Liturgical Press |isbn=978-0814658567 |pages=562–563}}</ref><ref>{{cite book |first=S. S. |last=Kumar |year=2010 |title=Bhakti – the Yoga of Love |publisher=LIT |location=Münster |isbn=978-3643501301 |pages=35–36}}</ref><ref name="donigerbrit">{{cite encyclopedia |first=Wendy |last=Doniger |year=2009 |url=http://www.britannica.com/EBchecked/topic/63933/bhakti |title=Bhakti |encyclopedia=Encyclopædia Britannica}}</ref><ref name="Surinder 1999">{{cite book|last1=Johar|first1=Surinder|title=Guru Gobind Singh: A Multi-faceted Personality|date=1999|publisher=MD Publications|isbn=978-8-175-33093-1|page=89}}</ref> भक्ति आंदोलन के संत आ प्रचारक लोग अपना उपदेश आ रचना सभ खातिर लोकल भाषा-बोली सभ के इस्तेमाल कइल लोग, जवना से कि ओह लोगन के संदेश आम जनता तक आसानी से पहुँच सके। एह आंदोलन के प्रेरणा अनेक कवि-संतन से मिलल, जिनकर विचारधारा बहुत विविध किसिम के रहल। एहमें द्वैत दर्शन के ईश्वरवादी मत से लेके [[अद्वैत वेदांत]] के पूर्ण अद्वैतवादी दर्शन तक के विचार शामिल रहल।{{sfnp|Schomer|McLeod|1987|p=2}}<ref name=novetzke>{{cite journal|author= Christian Novetzke |date= 2007 |title= Bhakti and Its Public |journal= International Journal of Hindu Studies |volume= 11 |number= 3 |pages= 255–272|jstor= 25691067 |doi= 10.1007/s11407-008-9049-9 |s2cid= 144065168 }}</ref> ई सगरी संत लोग भक्ति के माध्यम से आध्यात्मिक उन्नति के मार्ग बतावल लोग।
परंपरागत रूप से भक्ति आंदोलन के हिंदू धर्म के एगो प्रभावशाली सामाजिक सुधार आंदोलन मानल गइल बा, काहें से कि ई जनम, जाति भा लिंग से ऊपर उठ के हर ब्यक्ती खातिर आध्यात्मिकता के आपन निजी मार्ग उपलब्ध करावे ला।{{sfnp|Schomer|McLeod|1987|pp=1–2}} हालाँकि, कुछ आधुनिक बिद्वान लोग एह बात पर सवाल उठावेलन कि भक्ति आंदोलन वास्तव में सामाजिक सुधार भा विद्रोह के रूप में उभरल रहे कि ना। एह लोगन के हिसाब से, भक्ति आंदोलन के प्राचीन वैदिक परंपरा के दुबारा जिंदा होखे (रिवाइवल), पुनर्व्याख्या (रिवार्किंग) आ नया सामाजिक संदर्भ में पुनर्स्थापन (रीकॉन्टेक्स्चुअलाइजेशन) के रूप में समझल-मानल जा सकेला।{{sfnp|Pechilis Prentiss|2014|pages= 15-16}}
{{clear}}
== इहो देखल जाय ==
* [[सूफीवाद]]
== टीका-टिप्पणी ==
{{reflist|group=note}}
{{notelist}}
== फुटनोट आ संदर्भ ==
{{Reflist|29em}}
== किताबी स्रोत आ ग्रंथ सूची ==
{{refbegin|29em}}
{{refend}}
== बाहरी कड़ी ==
{{Commons category|Bhakti movement}}
{{Wikiquote|Bhakti movement}}
* [http://sites.fas.harvard.edu/~fc12/Bibliography/09_Bhakti_Bibliography.html Bhakti bibliography] {{Webarchive|url=https://web.archive.org/web/20160304123833/http://sites.fas.harvard.edu/~fc12/Bibliography/09_Bhakti_Bibliography.html |date=4 March 2016 }}, Harvard University Archive (2001)
* [[Wikisource: The Complete Works of Swami Vivekananda/Volume 3/Bhakti-Yoga/Definition of Bhakti|Definition of Bhakti]], Swami Vivekananda, Wikisource
{{हिंदू धर्म बिसय}}
[[श्रेणी:भक्ति आंदोलन| ]]
[[श्रेणी:हिंदू धर्म के इतिहास]]
[[श्रेणी:हिंदू धर्म]]
[[श्रेणी:सामाजिक आंदोलन]]
{{hinduism-stub}}
mtzoc1vj4m8kl547lpfsyuq5pyxvcfx
797108
797107
2026-06-08T19:45:01Z
SM7
3953
अंग्रेजी विकिपीडिया से अनुबाद क के बिस्तार कइल गइल
797108
wikitext
text/x-wiki
[[File:Meerabai (crop).jpg|thumb|[[कृष्ण]] भक्ती आंदोलन के मुख्य हिस्सा रहल बाड़ें। इनके एगो प्रमुख भक्त [[मीराबाई]] रहली (फोटो में देखावल)<ref name="smpandey">{{cite journal|author= SM Pandey |date= 1965 |title= Mīrābāī and Her Contributions to the Bhakti Movement |journal= History of Religions |volume= 5 |number= 1 |pages= 54–73|jstor= 1061803 |doi= 10.1086/462514 |s2cid= 162398500 }}</ref>]]
'''भक्ति आंदोलन''' मध्यकालीन [[हिंदू धर्म]] के एगो महत्त्वपूर्ण धार्मिक आंदोलन रहे,{{sfnp|Schomer|McLeod|1987|p=1}} जेकर मकसद [[भक्ति]] के माध्यम से [[मोक्ष]] (मुक्ती) प्राप्ति के मार्ग अपना के<ref name="cbseindiatoday">{{Cite web|url=https://www.indiatoday.in/education-today/gk-current-affairs/story/-crashcourse-cbse-class-12-history-bhakti-movement-s-emergence-and-influence-1438286-2019-01-24|title=CBSE Class 12 History #CrashCourse: Bhakti movement's emergence and influence|last1=India Today Web Desk New|date=January 24, 2019|website=India Today}}</ref> समाज के सभ वर्गन तक धार्मिक सुधार पहुँचावल रहे। एह आंदोलन के शुरुआत 6वीं सदी ईस्वी में{{sfn|Hawley|2015|p=87}}<ref>{{Cite book |last=Padmaja |first=T. |url=https://books.google.com/books?id=pzgaS1wRnl8C&dq=bhakti+movement+tamilakam&pg=RA1-PA37 |title=Temples of Kr̥ṣṇa in South India: History, Art, and Traditions in Tamil nāḍu |date=2002 |publisher=Abhinav |isbn=978-81-7017-398-4 |language=en}}</ref> [[तमिलकम]] (प्राचीन तमिल क्षेत्र) में भइल मानल जाला। दक्खिन भारत में शुरुआती मध्यकाल के दौरान वैष्णव आलवार आ शैव नायनार संत लोग के भक्ति-प्रधान कविता आ उपदेशन के माध्यम से एह आंदोलन के बिसेस पहिचान मिलल।{{sfnp|Schomer|McLeod|1987|p=1}} बाद में ई आंदोलन धीरे-धीरे उत्तर दिशा में फइलल आ भारत के बाकी अलग-अलग क्षेत्रन तक पहुँच गइल। 15वीं सदी के बाद भक्ति आंदोलन पूरबी आ उत्तरी भारत में तेजी से फइलल आ 15वीं से 17वीं सदी ईस्वी के बीच अपना चरम पर पहुँच गइल। ई आंदोलन धार्मिक आ सामाजिक जीवन पर गम्हीर प्रभाव डललस आ भक्ति के व्यक्तिगत आ सहज मार्ग के लोकप्रिय बनवलस।{{sfnp|Schomer|McLeod|1987|pp=1-2}}
भक्ति आंदोलन भारतीय उपमहादीप के अलग-अलग क्षेत्रन में अलग-अलग हिंदू देवी-देवतन के केंद्र में रख के बिकसित भइल। एह आंदोलन के कुछ प्रमुख धारा सभ में [[वैष्णव संप्रदाय|वैष्णव मत]] ([[विष्णु]] के उपासना), [[शैव मत]] ([[शिव]] के उपासना), [[शाक्त संप्रदाय|शाक्त मत]] ([[शक्ति (देवी)|शक्ति]] देवी के उपासना) आ [[स्मार्त|स्मार्त परंपरा]] शामिल रहल।<ref>{{cite book |first=Lance |last=Nelson |year=2007 |title=An Introductory Dictionary of Theology and Religious Studies |editor1-first=Orlando O. |editor1-last=Espín |editor2-first=James B. |editor2-last=Nickoloff |publisher=Liturgical Press |isbn=978-0814658567 |pages=562–563}}</ref><ref>{{cite book |first=S. S. |last=Kumar |year=2010 |title=Bhakti – the Yoga of Love |publisher=LIT |location=Münster |isbn=978-3643501301 |pages=35–36}}</ref><ref name="donigerbrit">{{cite encyclopedia |first=Wendy |last=Doniger |year=2009 |url=http://www.britannica.com/EBchecked/topic/63933/bhakti |title=Bhakti |encyclopedia=Encyclopædia Britannica}}</ref><ref name="Surinder 1999">{{cite book|last1=Johar|first1=Surinder|title=Guru Gobind Singh: A Multi-faceted Personality|date=1999|publisher=MD Publications|isbn=978-8-175-33093-1|page=89}}</ref> भक्ति आंदोलन के संत आ प्रचारक लोग अपना उपदेश आ रचना सभ खातिर लोकल भाषा-बोली सभ के इस्तेमाल कइल लोग, जवना से कि ओह लोगन के संदेश आम जनता तक आसानी से पहुँच सके। एह आंदोलन के प्रेरणा अनेक कवि-संतन से मिलल, जिनकर विचारधारा बहुत विविध किसिम के रहल। एहमें द्वैत दर्शन के ईश्वरवादी मत से लेके [[अद्वैत वेदांत]] के पूर्ण अद्वैतवादी दर्शन तक के विचार शामिल रहल।{{sfnp|Schomer|McLeod|1987|p=2}}<ref name=novetzke>{{cite journal|author= Christian Novetzke |date= 2007 |title= Bhakti and Its Public |journal= International Journal of Hindu Studies |volume= 11 |number= 3 |pages= 255–272|jstor= 25691067 |doi= 10.1007/s11407-008-9049-9 |s2cid= 144065168 }}</ref> ई सगरी संत लोग भक्ति के माध्यम से आध्यात्मिक उन्नति के मार्ग बतावल लोग।
परंपरागत रूप से भक्ति आंदोलन के हिंदू धर्म के एगो प्रभावशाली सामाजिक सुधार आंदोलन मानल गइल बा, काहें से कि ई जनम, जाति भा लिंग से ऊपर उठ के हर ब्यक्ती खातिर आध्यात्मिकता के आपन निजी मार्ग उपलब्ध करावे ला।{{sfnp|Schomer|McLeod|1987|pp=1–2}} हालाँकि, कुछ आधुनिक बिद्वान लोग एह बात पर सवाल उठावेलन कि भक्ति आंदोलन वास्तव में सामाजिक सुधार भा विद्रोह के रूप में उभरल रहे कि ना। एह लोगन के हिसाब से, भक्ति आंदोलन के प्राचीन वैदिक परंपरा के दुबारा जिंदा होखे (रिवाइवल), पुनर्व्याख्या (रिवार्किंग) आ नया सामाजिक संदर्भ में पुनर्स्थापन (रीकॉन्टेक्स्चुअलाइजेशन) के रूप में समझल-मानल जा सकेला।{{sfnp|Pechilis Prentiss|2014|pages= 15-16}}
== इतिहास ==
=== प्रमुख लोग ===
भक्ति आंदोलन के दौरान क्षेत्रीय भाषा सभ में हिंदू साहित्य के उल्लेखजोग विकास भइल, खासकर भक्ति कविता आ भजन-संगीत के रूप में। एह साहित्यिक परंपरा में आलवार आ नायनार संतन के रचना, आंडाल, बसव, भक्त पीपा, अल्लम प्रभु, अक्का महादेवी, वल्लभाचार्य, विट्ठलनाथ (गुसाईंजी), कबीर, गुरु नानक, तुलसीदास, नाभादास, घनानंद, रामानंद, रैदास, श्रीपादराज, व्यासतीर्थ, पुरंदर दास, कनकदास, विजय दास, वृंदावन के षट् गोस्वामी, रसखान, जयदेव, नामदेव, एकनाथ, तुकाराम, मीराबाई, रामप्रसाद सेन, शंकरदेव, नरसिंह मेहता, गंगासती आ चैतन्य महाप्रभु जइसन संतन के उपदेश आ रचना शामिल बाड़ी स।
आसाम में शंकरदेव के रचना में क्षेत्रीय भाषा पर विशेष जोर दिहल गइल, हालाँकि, एकरे बावजूद [[ब्रजावली]] नाँव के एगो आर्टिफिशियल साहित्यिक भाषा के विकास भइल। ब्रजावली मध्यकालीन मैथिली आ असमिया के मिलजुल रूप रहे। ई भाषा स्थानीय लोग खातिर आसानी से समझे लायक रहे, जे भक्ति आंदोलन के समावेशी भावना के अनुरूप रहे, जबकि एकरे चलते रचना सभ के साहित्यिक गरिमा भी बरकरार रहल। ठीक एही तरीका से, एगो अउरी आर्टिफिशियल साहित्यिक भाषा [[ब्रजबुली]] के लोकप्रिय बनावे के श्रेय [[विद्यापति]] के दिहल जाला। बाद में मध्यकाल में ओड़िशा के कई लेखक आ बंगाल के पुनर्जागरण काल के साहित्यकार लोग भी ब्रजबुली के अपनवलें। एह प्रकार भक्ति आंदोलन खाली धार्मिक चेतना के प्रसार तक सीमित ना रहल, बल्कि क्षेत्रीय भाषा, साहित्य आ सांस्कृतिक अभिव्यक्ति के विकास में भी महत्त्वपूर्ण योगदान दिहलस।
7वीं से 10वीं सदी के बीच के कुछ शुरुआती लेखक आ संत, जिनकर रचना बाद के कवि-संत आधारित भक्ति आंदोलन पर गहिर प्रभाव डाललस, ओहमें संबंदर, तिरुनावुक्करसर, सुंदरर, नम्मालवार, आदि शंकराचार्य, मणिक्कवाचकर आ नाथमुनि के नाम प्रमुख बा। एह लोग के उपदेश आ रचना दक्षिण भारत में भक्ति परंपरा के मजबूत आधार प्रदान कइलस। आगे चल के 11वीं आ 12वीं सदी में कई गो विद्वान संत लोग वेदांत परंपरा के भीतरें अलग-अलग दार्शनिक मत विकसित कइलें, जिनहन के मध्यकालीन भक्ति परंपरा पर बहुत प्रभाव पड़ल। एह विद्वानन में रामानुज, मध्वाचार्य, वल्लभाचार्य आ निंबार्क प्रमुख रहलें। एह लोग के विचारधारा ईश्वरवादी द्वैतवाद, विशिष्ट अद्वैत आ पूर्ण अद्वैत जइसन अलग-अलग दार्शनिक दृष्टिकोणन के प्रतिनिधित्व करे ला।
भक्ति आंदोलन के दौरान कई महत्त्वपूर्ण ग्रंथन के विभिन्न भारतीय भाषा सभ में अनुबादो भइल। उदाहरण खातिर, आदि शंकराचार्य द्वारा संस्कृत में रचल सौंदर्य लहरी के 12वीं सदी में विरै कविराज पंडितर तमिल में अनुबाद कइलें, जेकर नाम अभिरामी पाडल रखल गइल। एही तरह, वाल्मीकि के संस्कृत में लिखल रामायण के पहिलका इंडो-आर्य भाषा में अनुबाद [[माधव कंदली]] द्वारा असमिया भाषा में कइल गइल, जे ''सप्तकांड रामायण'' के नाम से प्रसिद्ध बा।
[[शांडिल्य]] आ [[नारद]] के रचल बतावल जाये वाला दू गो प्रसिद्ध भक्ति ग्रंथ — ''शांडिल्य भक्ति सूत्र'' आ ''नारद भक्ति सूत्र'' — भक्ति साहित्य के महत्त्वपूर्ण रचना मानल जालीं। हालाँकि, आधुनिक विद्वान लोग एह ग्रंथन के रचना काल लगभग 12वीं सदी के मानेलें।
{{clear}}
== इहो देखल जाय ==
* [[सूफीवाद]]
== टीका-टिप्पणी ==
{{reflist|group=note}}
{{notelist}}
== फुटनोट आ संदर्भ ==
{{Reflist|29em}}
== किताबी स्रोत आ ग्रंथ सूची ==
{{refbegin|29em}}
{{refend}}
== बाहरी कड़ी ==
{{Commons category|Bhakti movement}}
{{Wikiquote|Bhakti movement}}
* [http://sites.fas.harvard.edu/~fc12/Bibliography/09_Bhakti_Bibliography.html Bhakti bibliography] {{Webarchive|url=https://web.archive.org/web/20160304123833/http://sites.fas.harvard.edu/~fc12/Bibliography/09_Bhakti_Bibliography.html |date=4 March 2016 }}, Harvard University Archive (2001)
* [[Wikisource: The Complete Works of Swami Vivekananda/Volume 3/Bhakti-Yoga/Definition of Bhakti|Definition of Bhakti]], Swami Vivekananda, Wikisource
{{हिंदू धर्म बिसय}}
[[श्रेणी:भक्ति आंदोलन| ]]
[[श्रेणी:हिंदू धर्म के इतिहास]]
[[श्रेणी:हिंदू धर्म]]
[[श्रेणी:सामाजिक आंदोलन]]
{{hinduism-stub}}
kxgq9vns0nb9ufcgp56cl5kjvbuh6qc
श्रेणी:लेख जिनहन में हिंदी-भाषा के स्रोत बाटे (hi)
14
101015
797100
2026-06-08T14:27:35Z
SM7
3953
पन्ना बनावल गइल "{{Non-Bhojpuri-language sources category}}" के साथ
797100
wikitext
text/x-wiki
{{Non-Bhojpuri-language sources category}}
6co26q4quk3s6kll374dcojr2uyd9v0
विद्यालय
0
101016
797110
2026-06-08T19:56:27Z
SM7
3953
पन्ना [[इस्कूल]] पर अनुप्रेषित कइल गइल
797110
wikitext
text/x-wiki
#REDIRECT [[इस्कूल]]
pzjcdddn4t6hs6mfk1ankjbbkwrmr8f
797111
797110
2026-06-08T19:56:43Z
SM7
3953
Added {{[[:Template:R from alternative name|R from alternative name]]}} tag to redirect
797111
wikitext
text/x-wiki
#REDIRECT [[इस्कूल]]
{{Redirect category shell|
{{R from alternative name}}
}}
j0qp09safi5twdn8m0sixa0fdncpu3l
टेम्पलेट:Excerpt
10
101017
797114
2018-11-30T11:54:56Z
en>Sophivorus
0
Create template for [[Wikipedia:Cascading content]]. Will continue working on this asap
797114
wikitext
text/x-wiki
<includeonly><div class="excerpt-block"><div class="noprint rellink"><span style="font-size: smaller;">This section is transcluded from </span> ''[[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2|}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|#{{{section|{{{2|}}}}}}}}]]''</div><div class="excerpt">
{{#if:{{{fragmento|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragmento|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{refs|true}}}}}}}}}</div></includeonly><noinclude>{{Documentation}}</noinclude>
fnvll6hqfq8uz3qbt25mas54hj0vzq6
797115
797114
2018-11-30T12:22:37Z
en>Sophivorus
0
797115
wikitext
text/x-wiki
<includeonly><div class="excerpt-block"><div class="noprint rellink"><span style="font-size: smaller;">This section is transcluded from </span> ''[[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2|}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|#{{{section|{{{2|}}}}}}}}]]''</div><div class="excerpt">
{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{refs|true}}}}}}}}}</div></includeonly><noinclude>{{Documentation}}</noinclude>
86wpv6xmcxjurxzetgxqszm1pxg9a0l
797116
797115
2018-11-30T12:28:35Z
en>Sophivorus
0
797116
wikitext
text/x-wiki
<includeonly><div class="excerpt-block"><div class="noprint rellink"><span style="font-size: smaller;">This section is transcluded from </span> ''[[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2|}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|#{{{section|{{{2|}}}}}}}}]]''</div><div class="excerpt">
{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></includeonly><noinclude>{{Documentation}}</noinclude>
0ob4leyvz6bbyj2ffkdyoeigfduup6d
797117
797116
2019-11-13T12:50:16Z
en>Sophivorus
0
Update template
797117
wikitext
text/x-wiki
<noinclude><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span class="mw-editsection-bracket">]</span></span>|selfref=yes}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Wikipedia:Articles with extracts]]}}</noinclude><noinclude>{{Documentation}}</noinclude>
f7gsn0g9pq7te0lfd9qrq15p4prxj09
797118
797117
2019-11-13T12:50:53Z
en>Sophivorus
0
797118
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span class="mw-editsection-bracket">]</span></span>|selfref=yes}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Wikipedia:Articles with extracts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
jm98pi9f8yvzissyawtdghos8nh9uz7
797119
797118
2019-11-13T12:52:16Z
en>Sophivorus
0
797119
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span class="mw-editsection-bracket">]</span></span>''|selfref=yes}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Wikipedia:Articles with extracts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
iuhte47zv5hxjgds3k1ghnt75w7jik2
797120
797119
2019-11-13T13:23:58Z
en>Sophivorus
0
797120
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span class="mw-editsection-bracket">]</span></span>''|selfref=yes}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with extracts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
o8y67nh6owvo4j5pa754hwycil6s0wb
797121
797120
2019-11-13T13:24:37Z
en>Sophivorus
0
797121
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span class="mw-editsection-bracket">]</span></span>''|selfref=yes}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
i09wz7hdalm3u7dam2sxdmyd8y42if1
797122
797121
2019-11-13T13:31:13Z
en>Sophivorus
0
797122
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span class="mw-editsection-bracket">]</span></span>''|selfref=yes}}<div class="excerpt">
{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
d7wy2a4xf8rj3t8nxnqg7jzwsbm2n0m
797123
797122
2019-11-13T13:42:30Z
en>Sophivorus
0
Add link to history
797123
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span class="mw-editsection-bracket">]</span> <span class="mw-editsection-bracket">[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span class="mw-editsection-bracket">]</span></span>''|selfref=yes}}<div class="excerpt">
{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
s88uth7qr0c11o0307hx86suy7tbeap
797124
797123
2019-11-13T13:44:38Z
en>Sophivorus
0
797124
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}<div class="excerpt">
{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
guhxybu2197zgoyjmopjnan4yutjkbv
797125
797124
2019-11-13T14:39:01Z
en>Sophivorus
0
Wrap extract with [[Template:Trim]] to ensure no extra line breaks
797125
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|refs={{{references|true}}}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
gwf8djxrdrdv0ltbmc597wxqvihrc2g
797126
797125
2019-11-13T15:34:40Z
en>Sophivorus
0
Transclude main image too
797126
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{Transclude lead excerpt|1={{{article|{{{1}}}}}}|files=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
7rxr8wlly4wbiolgsanz78tn4uradxa
797127
797126
2020-01-07T18:00:03Z
en>Sophivorus
0
Replace template for plain invoke for better control
797127
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|files=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
f98lx5b6govvnxo0y0ddkgssfp2tppo
797128
797127
2020-01-07T21:54:49Z
en>Sophivorus
0
Replace #lsth for [[Module:Excerpt]]
797128
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}|files=1|keepRefs=1}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|files=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
o3dvzceaa2bwzgs1ubsbvhnpcaj3f1r
797129
797128
2020-01-07T21:58:05Z
en>Sophivorus
0
Return to #lsth until the module no longer removes tables
797129
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|files=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
f98lx5b6govvnxo0y0ddkgssfp2tppo
797130
797129
2020-03-17T19:55:26Z
en>Sdkb
0
Adding nohat parameter
797130
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#ifeq: {{{nohat}}} | y | | {{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|files=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
s6m2i2kvgc2w93r5kc5kik463ohq5cp
797131
797130
2020-03-17T22:01:59Z
en>Sophivorus
0
Improve nohat param
797131
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2|}}}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|files=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
8kojsdu0y0q9cmh9ijxyz3ln956ayju
797132
797131
2020-03-20T13:42:05Z
en>Sophivorus
0
Add option to select what paragraphs to filter, see talk page
797132
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepRefs=1|keepTables=1|1={{{article}}}#{{{section}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
gs139xad1h209xhbrak57rtvndfnfkd
797133
797132
2020-03-20T13:44:01Z
en>Sophivorus
0
797133
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepRefs=1|keepTables=1|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
65cw1oh9o6c7clok89paoy9ilhfgmz0
797134
797133
2020-03-20T16:19:53Z
en>SporkBot
0
Clean up [[:Category:Pages using duplicate arguments in template calls|duplicate template arguments]], marking duplicate parameter for repair
797134
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#invoke:Excerpt|lead|DUPLICATE-1={{{article|{{{1}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepRefs=1|keepTables=1|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
kwv8f68y62p8t6enpkbfsk54taxke60
797135
797134
2020-03-21T11:56:37Z
en>Sophivorus
0
Fix duplicate parameter
797135
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment|}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepRefs=1|keepTables=1}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
22j9imljvq3d042ksjxkchhtl2n1w6c
797136
797135
2020-03-27T02:47:56Z
en>Sophivorus
0
Add detection of broken excerpts
797136
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#if:{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|[[Category:Articles with broken excerpts]]}}|{{#if:{{{section|{{{2|}}}}}}|{{#if:{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}|[[Category:Articles with broken excerpts]]}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
nem1dnhapfcnehopeowcmuofg9er61d
797137
797136
2020-04-01T21:02:40Z
en>Sophivorus
0
Update to catch up with the latest version of [[Module:Excerpt]]
797137
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#if:{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|[[Category:Articles with broken excerpts]]}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
2y9zws3bu373ul132cyopbeuz4pvbyo
797138
797137
2020-04-03T17:40:32Z
en>Sophivorus
0
797138
wikitext
text/x-wiki
<includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
b7sykvvdib82o5yyiciukwwd847u46b
797139
797138
2020-04-04T11:49:38Z
en>Xaosflux
0
<templatestyles src="myTemplate/styles.css" />
797139
wikitext
text/x-wiki
<templatestyles src="myTemplate/styles.css" /><includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
s1k43gedegbzl1cqc9aom9eekk2622h
797140
797139
2020-04-04T11:49:59Z
en>Xaosflux
0
ts
797140
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">
{{trim|{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#invoke:Excerpt|lead|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}|paragraphs={{{paragraphs|}}}|files={{{files|1}}}|keepTables=1|keepRefs=1}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
lebpv1e3bkkox69m4acvbd35d9hsrb4
797141
797140
2020-04-04T14:31:24Z
en>Sophivorus
0
Add parameter to transclude subsections and remove bold text from lead excerpts
797141
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#if:{{{subsections|}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|nobold=1|1={{{article|{{{1}}}}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
1c52hqyh1c4d9v5vmj560052j467avf
797142
797141
2020-04-04T14:47:06Z
en>Xaosflux
0
Protected "[[Template:Excerpt]]": [[WP:High-risk templates|Highly visible template]] ([Edit=Require autoconfirmed or confirmed access] (indefinite) [Move=Require autoconfirmed or confirmed access] (indefinite))
797141
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><div class="excerpt-block">{{#if:{{{nohat|}}}||{{hatnote|1=This section is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#if:{{{subsections|}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|nobold=1|1={{{article|{{{1}}}}}}}}}}}}</div></div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
1c52hqyh1c4d9v5vmj560052j467avf
797143
797142
2020-04-04T15:17:45Z
en>Sophivorus
0
Add optional parameter to indicate where the excerpt starts and finishes
797143
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><div class="excerpt-block">{{#if:{{{indicator|}}}|<div style="border-left: 3px solid #c8ccd1; margin: 1em 0; padding-left: 1em;">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#if:{{{subsections|}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|nobold=1|1={{{article|{{{1}}}}}}}}}}}}</div>{{#if:{{{marcador|}}}|</div>}}</div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
nsiashh9j8pvfls2g8zefgf63w6gws5
797144
797143
2020-04-04T15:19:17Z
en>Sophivorus
0
797144
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><div class="excerpt-block">{{#if:{{{indicator|}}}|<div style="border-left: 3px solid #c8ccd1; margin: 1em 0; padding-left: 1em;">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#if:{{{subsections|}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|nobold=1|1={{{article|{{{1}}}}}}}}}}}}</div>{{#if:{{{indicator|}}}|</div>}}</div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
5hij8cwi8acx1nfld7gymltqlwua4ce
797145
797144
2020-04-04T16:59:16Z
en>Izno
0
swap to use class in the indicator also
797145
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><div class="excerpt-block">{{#if:{{{indicator|}}}|<div class="excerpt-indicator">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<div class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#if:{{{subsections|}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|nobold=1|1={{{article|{{{1}}}}}}}}}}}}</div>{{#if:{{{indicator|}}}|</div>}}</div>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
obiggbnfbwxexu2p8cq358236yrw7no
797146
797145
2020-04-07T00:41:03Z
en>Sdkb
0
Adding parameter to allow use of "span" rather than "div" to avoid unwanted line breaks
797146
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><{{{tag|div}}} class="excerpt-block">{{#if:{{{indicator|}}}|<{{{tag|div}}} class="excerpt-indicator">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<{{{tag|div}}} class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#if:{{{section|{{{2|}}}}}}|{{#if:{{{subsections|}}}|{{#lsth:{{{article|{{{1}}}}}}|{{{section|{{{2}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|1={{{article|{{{1}}}}}}#{{{section|{{{2}}}}}}}}}}|{{#invoke:Excerpt|lead|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables=1|keepRefs=1|nobold=1|1={{{article|{{{1}}}}}}}}}}}}</{{{tag|div}}}>{{#if:{{{indicator|}}}|</{{{tag|div}}}>}}</{{{tag|div}}}>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
ajrqz9xr9pole1bedd8atiyjxhaa8wl
797147
797146
2020-04-08T01:56:24Z
en>Sophivorus
0
797147
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><{{{tag|div}}} class="excerpt-block">{{#if:{{{indicator|}}}|<{{{tag|div}}} class="excerpt-indicator">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an [[Wikipedia:Excerpts|excerpt]] from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<{{{tag|div}}} class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#invoke:Excerpt|lead|nobold=1|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables={{{tables|1}}}|keepRefs={{{references|1}}}|keepSubsections={{{subsections|}}}|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}}}}}</{{{tag|div}}}>{{#if:{{{indicator|}}}|</{{{tag|div}}}>}}</{{{tag|div}}}>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
tar8s1m593lzro6tk1xrmj7ldoqntm9
797148
797147
2020-04-21T21:14:54Z
en>Sdkb
0
/* top */ Removing link to [[WP:Excerpts]] (see talk)
797148
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><{{{tag|div}}} class="excerpt-block">{{#if:{{{indicator|}}}|<{{{tag|div}}} class="excerpt-indicator">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an excerpt from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span>]</span> <span>[</span>[{{fullurl:{{{1|{{{article}}}}}}|action=history}} history]<span>]</span></span>''|selfref=yes}}}}<{{{tag|div}}} class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#invoke:Excerpt|lead|nobold=1|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables={{{tables|1}}}|keepRefs={{{references|1}}}|keepSubsections={{{subsections|}}}|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}}}}}</{{{tag|div}}}>{{#if:{{{indicator|}}}|</{{{tag|div}}}>}}</{{{tag|div}}}>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
ey342liny37qts35997rm71n99h5lnh
797149
797148
2020-04-21T21:42:51Z
en>Sdkb
0
Fixing formatting to add space (same as is used for normal section edit links), and removing the history link, per [[WP:READER]]
797149
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><{{{tag|div}}} class="excerpt-block">{{#if:{{{indicator|}}}|<{{{tag|div}}} class="excerpt-indicator">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an excerpt from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[ </span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span> ]</span></span>''|selfref=yes}}}}<{{{tag|div}}} class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#invoke:Excerpt|lead|nobold=1|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables={{{tables|1}}}|keepRefs={{{references|1}}}|keepSubsections={{{subsections|}}}|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}}}}}</{{{tag|div}}}>{{#if:{{{indicator|}}}|</{{{tag|div}}}>}}</{{{tag|div}}}>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
sublxbzmlcoodh76uksxlcs3rua56ze
797150
797149
2020-04-22T01:15:03Z
en>Oshwah
0
Changed protection level for "[[Template:Excerpt]]": [[WP:High-risk templates|Highly visible template]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
797149
wikitext
text/x-wiki
<templatestyles src="Excerpt/styles.css" /><includeonly><{{{tag|div}}} class="excerpt-block">{{#if:{{{indicator|}}}|<{{{tag|div}}} class="excerpt-indicator">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an excerpt from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[ </span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span> ]</span></span>''|selfref=yes}}}}<{{{tag|div}}} class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#invoke:Excerpt|lead|nobold=1|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables={{{tables|1}}}|keepRefs={{{references|1}}}|keepSubsections={{{subsections|}}}|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}}}}}</{{{tag|div}}}>{{#if:{{{indicator|}}}|</{{{tag|div}}}>}}</{{{tag|div}}}>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
sublxbzmlcoodh76uksxlcs3rua56ze
797151
797150
2020-04-22T01:15:03Z
en>Oshwah
0
Adding {{pp-template}} ([[WP:TW|TW]])
797151
wikitext
text/x-wiki
<noinclude>{{pp-template|small=yes}}</noinclude><templatestyles src="Excerpt/styles.css" /><includeonly><{{{tag|div}}} class="excerpt-block">{{#if:{{{indicator|}}}|<{{{tag|div}}} class="excerpt-indicator">}}{{#if:{{{nohat|}}}||{{hatnote|1={{#if:{{{indicator|}}}|This|This section}} is an excerpt from [[{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|{{urlencode:#{{{section|{{{2}}}}}}|PATH}}}}|{{{article|{{{1}}}}}}{{#if:{{{section|{{{2|}}}}}}|<nowiki> § </nowiki>{{{section|{{{2}}}}}}}}]]''<span class="mw-editsection-like plainlinks"><span>[ </span>[{{fullurl:{{{1|{{{article}}}}}}|action=edit}} edit]<span> ]</span></span>''|selfref=yes}}}}<{{{tag|div}}} class="excerpt">{{#if:{{{fragment|}}}|{{#lst:{{{article|{{{1}}}}}}|{{{fragment}}}}}|{{#invoke:Excerpt|lead|nobold=1|files={{{files|1}}}|paragraphs={{{paragraphs|}}}|keepTables={{{tables|1}}}|keepRefs={{{references|1}}}|keepSubsections={{{subsections|}}}|1={{{article|{{{1}}}}}}#{{{section|{{{2|}}}}}}}}}}</{{{tag|div}}}>{{#if:{{{indicator|}}}|</{{{tag|div}}}>}}</{{{tag|div}}}>{{#switch:{{NAMESPACENUMBER}}|0=[[Category:Articles with excerpts]]}}</includeonly><noinclude>{{Documentation}}</noinclude>
ra312p93bo2zt8v1cyuxfgzvhuub3iu
797152
797151
2020-04-23T18:11:15Z
en>Ahecht
0
Switch to lua
797152
wikitext
text/x-wiki
<noinclude>{{pp-template|small=yes}}</noinclude>{{#invoke:Excerpt/sandbox|excerpt}}<noinclude>
{{Documentation}}
</noinclude>
gcceloaj1j37k55baeat68em80qkeid
797153
797152
2020-04-23T18:11:48Z
en>Ahecht
0
<includeonly>
797153
wikitext
text/x-wiki
<noinclude>{{pp-template|small=yes}}</noinclude><includeonly>{{#invoke:Excerpt/sandbox|excerpt}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
fincy7q6dcbi3ehygtszpwh6zzdwec6
797154
797153
2020-04-23T19:56:31Z
en>Ahecht
0
rm /sandbox
797154
wikitext
text/x-wiki
<noinclude>{{pp-template|small=yes}}</noinclude><includeonly>{{#invoke:Excerpt|excerpt}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
q73h25ptjta5eyy8d20nd72srxk22or
797155
797154
2020-05-13T13:16:51Z
en>Sophivorus
0
Update from latest sandbox version
797155
wikitext
text/x-wiki
<noinclude>{{pp-template|small=yes}}</noinclude><includeonly>{{#invoke:Excerpt/templates|excerpt}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
cb8nynxkpk8n1ft0p3v26gimj8f2ejt
797156
797155
2020-05-13T13:21:58Z
en>Pppery
0
Please stop misusing your global interface editor access to edit template-protected templates
797156
wikitext
text/x-wiki
<noinclude>{{pp-template|small=yes}}</noinclude><includeonly>{{#invoke:Excerpt|excerpt}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
q73h25ptjta5eyy8d20nd72srxk22or
797157
797156
2020-06-01T12:50:37Z
en>Sophivorus
0
Update to new version
797157
wikitext
text/x-wiki
<includeonly>{{#invoke:Excerpt/templates|excerpt}}</includeonly><noinclude>{{pp-template|small=yes}}{{Documentation}}</noinclude>
goy0ll0n9utyhprzqj51maqkq4pw09d
797158
797157
2020-08-28T12:35:39Z
en>Sophivorus
0
Update to latest, see [[Module talk:Excerpt#New version]]. Going through necessary but temporary /staging submodule
797158
wikitext
text/x-wiki
<includeonly>{{#invoke:Excerpt/staging|main
| class = {{#if:{{{indicator|}}}|excerpt-indicator|{{{class|}}}}}
| hat = {{#if:{{{nohat|}}}|0|{{{hat|}}}}}<!-- Backwards compatibility -->
| inline = {{#ifeq:{{{tag|}}}|span|1|{{{inline|}}}}}<!-- Backwards compatibility -->
| sections = {{{subsections|{{{sections|}}}}}}<!-- Backwards compatibility -->
}}</includeonly><noinclude>{{pp-template|small=yes}}{{Documentation}}</noinclude>
5j6p4dzhz9tarirmu8hlzfjr713ijm2
797159
797158
2020-08-29T15:07:53Z
en>Sophivorus
0
Move from staging to production version
797159
wikitext
text/x-wiki
<includeonly>{{#invoke:Excerpt|main
| class = {{#if:{{{indicator|}}}|excerpt-indicator|{{{class|}}}}}
| hat = {{#if:{{{nohat|}}}|0|{{{hat|}}}}}<!-- Backwards compatibility -->
| inline = {{#ifeq:{{{tag|}}}|span|1|{{{inline|}}}}}<!-- Backwards compatibility -->
| sections = {{{subsections|{{{sections|}}}}}}<!-- Backwards compatibility -->
}}</includeonly><noinclude>{{pp-template|small=yes}}{{Documentation}}</noinclude>
fiwojhawj717ow8w57uene31wad18re
797160
797159
2022-01-20T20:09:16Z
en>Sophivorus
0
After replacing all deprecated parameters in all articles using them, I'm removing their backwards compatibility to simplify both code and documentation
797160
wikitext
text/x-wiki
<includeonly>{{#invoke:Excerpt|main}}</includeonly><noinclude>{{pp-template|small=yes}}{{Documentation}}</noinclude>
rp8yr72p8ph9ror00wcnysz9vezq15t
797161
797160
2022-01-21T20:09:52Z
en>Sophivorus
0
Add 'section', 'fragment', 'page' and 'article' as aliases because the new version of the module doesn't support aliases
797161
wikitext
text/x-wiki
<includeonly>{{#invoke:Excerpt|main
| 1 = {{{article|{{{page|{{{1}}}}}}}}}
| 2 = {{{section|{{{fragment|{{{2|}}}}}}}}}
}}</includeonly><noinclude>{{pp-template|small=yes}}{{Documentation}}</noinclude>
1i0qym567lcavwa7ckkeuai2og4fwsi
797162
797161
2022-07-08T12:04:35Z
en>Paine Ellsworth
0
not needed - provided by Documentation template
797162
wikitext
text/x-wiki
<includeonly>{{#invoke:Excerpt|main
| 1 = {{{article|{{{page|{{{1}}}}}}}}}
| 2 = {{{section|{{{fragment|{{{2|}}}}}}}}}
}}</includeonly><noinclude>{{Documentation}}</noinclude>
i0godpcyj2i0s4wm83my8ehm3bnfv5n
797163
797162
2026-06-08T20:19:11Z
SM7
3953
49 revisions imported from [[:en:Template:Excerpt]]
797162
wikitext
text/x-wiki
<includeonly>{{#invoke:Excerpt|main
| 1 = {{{article|{{{page|{{{1}}}}}}}}}
| 2 = {{{section|{{{fragment|{{{2|}}}}}}}}}
}}</includeonly><noinclude>{{Documentation}}</noinclude>
i0godpcyj2i0s4wm83my8ehm3bnfv5n
टेम्पलेट:Excerpt/doc
10
101018
797164
2026-06-02T19:04:17Z
en>Myselfthethird
0
Closing parenthesis
797164
wikitext
text/x-wiki
{{High-use}}
{{Documentation subpage}}
{{never subst}}
{{Lua|Module:Excerpt}}
This template is used for reusing parts of pages in other pages. This practice has various [[#Advantages and disadvantages|advantages and disadvantages]].
This template extends the capabilities of the built-in [[Help:Transclusion|normal transclusion]] and [[Help:Labeled section transclusion|labeled section transclusion]].
== Usage ==
* <code><nowiki>{{Excerpt|Page title}}</nowiki></code> – Transclude the lead section ([[Africa#Architecture|example]])
* <code><nowiki>{{Excerpt|Page title|Section title}}</nowiki></code> – Transclude a specific section, excluding any subsections ([[Eating#Mammals|example]])
== Parameters ==
There is one required parameter, and numerous optional ones for configuring the excerpt:
=== Summary ===
'''Source identification'''
* {{para|1}} – Name of the article or page to transclude. '''Required.''' Aliases: {{para|article}} or {{para|page}}.
* {{para|2}} – Name of the section or tag to transclude. Optional; if omitted, transcludes the lead section (content above the first section header). Aliases: {{para|section}} or {{para|fragment}}.
'''Transclusion config'''
Transcludable content is defined as one of several ''element types'': {{pval|file}}, {{pval|list}}, {{pval|paragraph}}, {{pval|reference}}, {{pval|subsection}}, {{pval|table}}, or {{pval|template}}.
Config parameters specify which ''element type'' to transclude, and in some cases, ''how many'' and ''which'' items of that type to transclude. All config parameters are optional; if omitted, all items of all element types are transcluded from the source page identified by the two unnamed parameters. Some element types support conditional item transclusion by specifying an item number range (1-3) or comma series (1, 2, 5); these types include: files, lists, paragraphs, and tables.
There are ten optional transclusion configuration parameters:
* {{para|only}} – Element types to transclude. Values: {{pval|file(s)}}, {{pval|list(s)}}, {{pval|table(s)}}, {{pval|template(s)}}, {{pval|paragraph(s)}}. Default: all element types.
* {{para|files}} – [[WP:FILE|Files]] to transclude. Default: all files. Same basic syntax as {{para|paragraphs}}, but see {{slink||Details}}.
** {{para|onlyfreefiles|no}} – Enables transclusion of [[WP:Non-free content|non-free files]]. Default: exclude non-free content.
* {{para|lists}} – Lists ([[MOS:LIST#Bulleted lists|bulleted]], [[MOS:LIST#Numbered lists|numbered]]) to transclude. Default: all lists. Same syntax as for {{para|paragraphs}}.
* {{para|paragraphs}} – [[H:PARAGRAPH|Paragraphs]] to transclude. Default: all paragraphs.
* {{para|references|no}} – Exclude all [[WP:REF|references]] between <code><nowiki><ref>...</ref></nowiki></code> tags.
* {{para|subsections|yes}} – Include [[MOS:SECTIONS|subsections]] of the transcluded section. Default: only content above the first subsection header.
* {{para|tables}} – [[Help:Table|Tables]] to transclude. Default: all tables. Same basic syntax as {{para|paragraphs}}, but see {{slink||Details}}.
* {{para|templates}} – [[Help:Template|Templates]] to transclude. By default all templates are transcluded, except those blacklisted at [[Module:Excerpt/config]]. See {{slink||Details}} for how to specify a specific template or templates for inclusion or exclusion.
'''Style and extra features'''
These optional parameters alter the way transcluded items are displayed:
* {{para|bold|yes}} – Preserve '''bold''' text.
* {{para|briefdates|yes}} – Abbreviate birth and death information to (YYYY-YYYY) format
* {{para|displaytitle}} – Change the text of the link in the hatnote. For example, [[WP:ITHAT|add italics]], subscripts, etc.
* {{para|hat|no}} – Hide the [[Wikipedia:Hatnote|hatnote]] "This section is an excerpt from..."
* {{para|inline|yes}} – Remove the <code><nowiki><div></nowiki></code> tags around the excerpt, to use it inside other text, or to add references or other content after it [[#Suppress line breaks between paragraphs|with no paragraph break]] between them. Also hides the hatnote; to reestablish it, use {{tl|transclusion notice}} above the Excerpt invocation.
* {{para|links|no}} – Unlink all [[H:WIKILINK|wikilinks]] and render as plain text.
* {{para|quote|yes}} – Wrap the excerpt with <code><nowiki><blockquote></nowiki></code> tags.
* {{para|this}} – Change the initial text of the hatnote. For example, if the transcluded content is a gallery, you can set {{para|this|This gallery is}} so that the hatnote reads "This gallery is an excerpt from..."
=== Details ===
* {{para|1}} or {{para|article}} or {{para|page}}
*: By default the lead section is transcluded ([[Africa#Water|example]]). If the page contains an infobox, the image and caption of the infobox will be transcluded (unless {{para|files|0}} is set). Also, templates listed at [[Module:Excerpt/config]] will not be transcluded (unless requested explicitly with {{para|templates}}, see below).
* {{para|2}} or {{para|section}} or {{para|fragment}}
*: Name of the section to transclude ([[Eating#Mammals|example]]) or of the <code><nowiki><section></nowiki></code> tag to transclude ([[Axiom Space#Ax-1|example]]). In the case of a section tag, the section must be marked with <code><nowiki><section begin="Name of the fragment" /></nowiki></code> and <code><nowiki><section end="Name of the fragment" /></nowiki></code> in the page to be transcluded. Notice that this template provides other ways of targeting specific fragments of a page without having to resort to section tags.
* {{para|only}}
*: The ''element type'' to transclude, excluding all other types. By default all element types are transcluded. Param {{para|only}} is an exclusionary param, and excludes all other types of elements, except the one you name, so that for example, specifying {{para|only|paragraphs}} excludes all lists, tables, templates, and so on. Param values can be in the singular (e.g., {{para|only|paragraph}}) or plural (e.g., {{para|only|paragraphs}}) and mean different things: in the singular, only the first item of that element type is transcluded; in the plural, all items are.
*:* {{para|only|file}} – Transclude only the first file (but no lists, paragraphs, tables, etc.)
*:* {{para|only|files}} – Transclude all files (but nothing else)
*:* {{para|only|filename}} – Transclude only the file name (Example.jpg) of the first file (and nothing else). If no file is found, "Noimage.png" will be returned.
*:* {{para|only|list}} – Transclude only the first list, exclude all other element types
*:* {{para|only|lists}} – Transclude all lists (but nothing else)
*:* {{para|only|table}} – Transclude only the first table, exclude all other element types. Does not transclude table templates, use {{para|templates}} for that.
*:* {{para|only|tables}} – Transclude all tables (but nothing else) ([[JKT48#Singles|example]])
*:* {{para|only|template}} – Transclude only the first template (excluding templates blacklisted at [[Module:Excerpt/config]], as well as all other element types)
*:* {{para|only|templates}} – Transclude all templates (excluding blacklisted templates) (but nothing else)
*:* {{para|only|paragraph}} – Transclude only the first paragraph, exclude all other element types
*:* {{para|only|paragraphs}} – Transclude all paragraphs (but nothing else)
* {{para|files}}
*: An ''element item'' param, defining which files, such as images and other media, to transclude. Default: all files.
*:* {{para|files|0}} – Transclude no files
*:* {{para|files|A.jpg}} – Transclude the file named 'A.jpg'
*:* {{para|files|A.jpg, B.png, C.gif}} – Transclude the files named 'A.jpg', 'B.png' and 'C.gif'
*:* {{para|files|.+%.png}} – Transclude all PNG files
*:* {{para|files|-A.jpg}} – Transclude all files except the one named 'A.jpg'
*:* {{para|files|-A.jpg, B.png, C.gif}} – Transclude all files except the ones named 'A.jpg', 'B.png' and 'C.gif'
*:* {{para|files|-.+%.png}} – Transclude all non-PNG files
* {{para|paragraphs}}
*: An ''element item'' param, defining which paragraphs to transclude. By default all paragraphs are transcluded.
*:* {{para|paragraphs|0}} – Transclude no paragraphs
*:* {{para|paragraphs|1}} – Transclude the first paragraph
*:* {{para|paragraphs|2}} – Transclude the second paragraph
*:* {{para|paragraphs|1,3}} – Transclude the first and third paragraphs
*:* {{para|paragraphs|1-3}} – Transclude the first, second and third paragraphs
*:* {{para|paragraphs|1-3,5}} – Transclude the first, second, third and fifth paragraphs
*:* {{para|paragraphs|-1}} – Transclude all paragraphs except the first
*:* {{para|paragraphs|-2}} – Transclude all paragraphs except the second
*:* {{para|paragraphs|-1,3}} – Transclude all paragraphs except the first and third
*:* {{para|paragraphs|-1-3}} – Transclude all paragraphs except the first, second and third
*:* {{para|paragraphs|-1-3,5}} – Transclude all paragraphs except the first, second, third and fifth
* {{para|subsections|yes}}
*: An ''element item'' param, defining which subsections of the source to transclude. Default: only the portion of the source lying above the first subsection header. Notice that if the transclusion is done from a section level 3 in the transcluding page, and the transcluded subsections are also level 3, then transcluded subsections will show with the same hierarchy as the transcluding section, which may not be the desired outcome, so use with caution.
* {{para|tables}}
*: An ''element item'' param, defining which tables to transclude. Default: all tables. Does not transclude table templates just above the tables. Same syntax as when transcluding paragraphs, but also:
*:* {{para|tables|Stats2020}} – Transclude the table with id 'Stats2020'
*:* {{para|tables|Stats2020, Stats2019, Stats2018}} – Transclude the tables with ids 'Stats2020', 'Stats2019' and 'Stats2018'
*:* {{para|tables|-Stats2020}} – Transclude all tables except the one with id 'Stats2020'
*:* {{para|tables|-Stats2020, Stats2019, Stats2018}} – Transclude all tables except the ones with ids 'Stats2020', 'Stats2019' and 'Stats2018'
* {{para|templates}}
*: An ''element item'' param, defining which templates to transclude. Default: all templates except those blacklisted at [[Module:Excerpt/config]]. Using a hyphen (minus sign) before a comma-delimited list of templates excludes those templates from transclusion.
*:* {{para|templates|-Ocean}} – Add the template 'Ocean' to the blacklist
*:* {{para|templates|-Ocean, Nature}} – Add the templates 'Ocean' and 'Nature' to the blacklist
*:* {{para|templates|Infobox person}} – Ignore the blacklist and transclude the template 'Infobox person'
*:* {{para|templates|Infobox person, Ocean}} – Ignore the blacklist and transclude the templates 'Infobox person' and 'Ocean'
*:* {{para|templates|.*}} – Ignore the blacklist and transclude all templates
== Tips and how-to ==
=== Compared to #section ===
{{Further|Help:Transclusion#Standard section transclusion}}
For simple cases of transcluding sections of articles, the {{pf|section}}, {{pf|section-x}}, and {{pf|section-h}} (abbreviated {{pf|lst}}, {{pf|lstx}}, and {{pf|lsth}})) parser functions can be used instead of this template. {{pf|lsth|''article''|''fragmentname''}} will transclude the section of "''article''" with the header "''fragmentname''", and {{pf|lsth|''article''}} will transclude the lead section of "''article''". Excerpting only specific paragraphs can be done by marking up the source article with <code><nowiki><section begin=</nowiki>''<nowiki>fragmentname</nowiki>''<nowiki>/>...<section end=</nowiki>''<nowiki>fragmentname</nowiki>''<nowiki>/></nowiki></code> tags and using {{pf|lst|''article''|''fragmentname''}} to transclude those fragments, which is equivalent to using the {{para|fragment|''fragmentname''}} parameter with this template. {{pf|lsth|''article''|''fragmentname''}} can also be used to transclude everything ''but'' those fragments.
The text will not be trimmed of excess whitespace, there will not be a header (equivalent to {{para|hat|no}}), and all files, templates, tables, references, and subsections will be included unless the source article is marked up with <code><nowiki><section begin=</nowiki>''<nowiki>fragmentname</nowiki>''<nowiki>/>...<section end=</nowiki>''<nowiki>fragmentname</nowiki>''<nowiki>/></nowiki></code>, {{tag|noinclude}}, or {{tag|onlyinclude}} tags. [[Help:Self link|Self links]] will appear in bold.
=== Citations ===
==== Differing styles ====
{{Further|Wikipedia:Citing sources#Variation in citation methods}}
It can happen that the source you want to excerpt contains footnotes in a [[WP:CITEVAR|different citation style]] than your article, and excerpting the source would cause a citation style mismatch, which is contrary to the guideline on [[Wikipedia:Citing sources|citing sources]]. Sometimes, excerpt can still be used, while avoiding a mismatch in style, by the use of params {{para|references|no}} and {{para|inline|yes}}.
If the source you want to excerpt has multiple ref-tags interspersed throughout the source, and they need to display exactly in those locations in order to maintain full [[WP:V|verifiability]], then this source might not be a good candidate for transclusion via the {{tl|excerpt}} template, and [[WP:CWW|copying the content]] from the source into the article might be a better choice.
However, you can still use the Excerpt template, if the source page you want to excerpt meets '''either''' of these conditions:
* references are found mostly or all at the end of the text to be excerpted; or
* references are scattered throughout, but could legitimately be regrouped at the end of the excerpt without adversely affecting [[WP:V|verifiability]].
In either case, use params {{para|references|no}} to strip the ref-tags from the transcluded content, and {{para|inline|yes}} to define the excerpt as an [[HTML element#Inline elements|inline display element]] in order to {{slink||Suppress line breaks between paragraphs}}, and then manually append a copy of all the citations in the source immediately after the excerpt tag ending curly braces in the target article, with no intervening line breaks, white space, or other characters between the tag and the appended references. The copied references will have to be manually converted from [[Wikipedia:Citing sources#Short and full citations|short footnote-style to full, inline citation-style]], or vice-versa, to match the citation style of the target. '''Note:''' citations are not creative content, and [[Wikipedia:Copying within Wikipedia#Where attribution is not needed|attribution is not needed]] for copying them.
==== Another approach ====
It is advised to seek consensus before attempting this layout change.
If there are few shortened footnotes, a new subsection can be added to the "References" section to allow {{tl|sfn}} to link to the correct citations. Here is one approach, where the {{tl|sfn}} citations list in the excerpted article "SCBA" is titled "Works cited":
<syntaxhighlight lang="wikitext" highlight="7-8">
== Transcluded section ==
{{excerpt|SCBA}}
== References ==
{{reflist}}
=== From SCBA ===
{{excerpt|SCBA|Works cited|hat=0}}
</syntaxhighlight>
{{tl|sfn}} footnotes will now link to the citations created in the excerpted subsection.
==== Ref name collision ====
To prevent the possibility of collisions between [[WP:NAMEDREFS|named references]] in the transcluding and excerpted pages which would otherwise generate a [[Help:Cite errors/Cite error references duplicate key|duplicate key error]], all ref names in the excerpted content are altered by prefixing the name with the pagename of the excerpted page. Thus, if the ref on page {{kbd|MyArticle}} looks like <code><nowiki><ref name="Jones-2024"></nowiki></code>, then when excerpted by {{kbd|TargetPage}} it will be changed to <code><nowiki><ref name="MyArticle Jones-2024"></nowiki></code>, in order to avoid a collision with a possible "Jones-2024" ref already on the target page.
=== Excerpt trees ===
[[File:Excerpt tree.png|class=skin-invert-image|thumb|Visual representation of an imaginary excerpt tree.]]
When a very general article uses excerpts from more specific articles, which in turn use excerpts from even more specific articles, then a [[tree structure]] emerges, called an "Excerpt tree".
Here you can navigate the main excerpt trees on the English Wikipedia. It's useful for editors interested in expanding or improving them. To navigate the trees, click the following button<sup><abbr title="{{Int:gadgets-sister}}">(S)</abbr></sup>:
{{Clickable button|url={{fullurl:{{FULLPAGENAME}}#Excerpt trees|withJS=MediaWiki:ExcerptTree.js}} |2=See the excerpt trees}}
<div class="ExcerptTree" style="display: none;">
* [[January 6 United States Capitol attack]]
* [[Algae]]
* [[Africa]]
* [[Aquatic ecosystem]]
* [[Bangladesh]]
* [[Campanology]]
* [[Climate change in Africa]]
* [[Climate change in South Asia]]
* [[Climate justice]]
* [[COVID-19 pandemic]]
* [[Ecosystem]]
** [[Aquatic ecosystem]]
* [[Effects of climate change on humans]]
* [[Environmental impact of agriculture]]
* [[Eutrophication]]
* [[Fishing industry]]
* [[Food]]
* [[God]]
** [[Existence of God]]
* [[Hygiene]]
* [[List of carillons]]
* [[List of LGBT-related cases in the United States Supreme Court]]
* [[Marine debris]]
* [[Middle East]]
* [[Ocean]]
* [[Plastic pollution]]
* [[Public health]]
* [[Sanitation]]
* [[Sewage]]
* [[Sustainable Development Goals]]
* [[Tourism]]
* [[Wastewater treatment]]
* [[Water resources]]
</div>
=== Refinement using inclusion control ===
{{Further|Help:Template#Noinclude, includeonly, and onlyinclude}}
Sometimes, a passage will almost fit for a transclusion, but not quite. In these cases, you can edit the source page to add <code><nowiki><noinclude>...</noinclude></nowiki></code> tags around content you don't want in the excerpt and <code><nowiki><includeonly>...</includeonly></nowiki></code> tags around content you want only in the excerpt.
For instance, the page [[COVID-19 misinformation]] begins with "The [[COVID-19 pandemic]] has resulted in [[misinformation]]...". However, when excerpting this lead to the misinformation section of [[COVID-19 pandemic]], we don't need to specify which pandemic we're referring to. Therefore, the code <code><nowiki>The <noinclude>[[COVID-19 pandemic]]</noinclude><includeonly>pandemic</includeonly> has resulted in [[misinformation]]</nowiki></code> can be used at the misinformation page, so that it will appear at the pandemic page as "The pandemic has resulted in [[misinformation]]...".
For pages with a high volume of edits, it may be a good idea to leave a hidden comment explaining why the tags are there, so that no one will be [[WP:FENCE|tempted]] to remove them, like so: <code><nowiki>The <noinclude>[[COVID-19 pandemic]]</noinclude><!--These tags are used to refine the excerpt at [[COVID-19 pandemic]]--><includeonly>pandemic</includeonly> has resulted in [[misinformation]]</nowiki></code>
Please note that when the <code><nowiki></noinclude></nowiki></code> tag is wrapped into a new line, a character next to it would be interpreted as a line beginning. This can lead to some formatting problems. For example, when a <code><nowiki></noinclude></nowiki></code> at line beginning is succeeded by a [[whitespace character]], the page engine would translate this as a [[Help:Wikitext#Limiting formatting / escaping wiki markup|leading space]] that renders the subsequent paragraph in [[code block]] and [[monospaced font]] with preserved formatting. For this reason, no spaces should separate the <code><nowiki></noinclude></nowiki></code> tag from the text it precedes.
=== Replacing summary section with excerpt of child article ===
[[File:How to excerpt.webm|thumb|How to replace a section with an excerpt.]]
A section is often a [[WP:Summary style|summary]] in a [[WP:G#Parent article|parent article]] of a more detailed page about a subtopic located in a [[WP:G#child page|child page]]; these are generally linked with [[Template:Main]] in the parent. Sometimes it's convenient to replace the content of such a summary section in the parent with an excerpt of the child page lead (after merging any valuable content of the section into the child page). In such cases, an efficient way to proceed is:
# Open the parent section for editing in one tab, and the child article in another.
# Copy the text of the parent section and append it to the lead section of the child page.
# Consolidate and adjust the combined lead using common sense.
# Save the changes in the child article with an [[WP:ES|edit summary]] like: "{{xt|<nowiki>Copied content from [[Page]]. See that article's history for attribution."</nowiki>}}
# Back in the parent page section, delete all content except the section header and replace it with an excerpt of the child page.
# Save the changes in the section; proposed edit summary: "{{xt|Moved section content to <nowiki>[[Child page title]]</nowiki> and replaced with excerpt.}}"
'''IMPORTANT!''' In step four, include the '''full''' edit summary as shown to comply with Wikipedia's [[Wikipedia:Copying within Wikipedia|copyright policy]]. If you forget to do this at the time of the original edit, follow the instructions on [[Wikipedia:Copying within Wikipedia#Repairing insufficient attribution|Repairing insufficient attribution]] to create a dummy edit with the required edit summary.
=== Suppress line breaks between paragraphs ===
If you want to merge two excerpted paragraphs from a source into one longer one in your article, use two excerpts instead of one, and change the display mode to [[HTML element#Inline elements|inline]]. So, for example, instead of this :
: {{tlc|excerpt|Ocean color|paragraphs{{=}}2-3|file{{=}}no}} // (example taken from [[Ocean#Color]])
you could code:
: {{tlc|excerpt|Ocean color|paragraphs{{=}}2|file{{=}}no|inline{{=}}yes}}
: {{tlc|excerpt|Ocean color|paragraphs{{=}}3|file{{=}}no|inline{{=}}yes}}
and this will remove the [[Help:Line-break handling#Newlines|line break]] between the two paragraphs, so they will render as one paragraph.
By default, an {{tl|excerpt}} generates an HTML [[div and span#Differences and default behaviour|div-tag]], which is a [[HTML element#Block elements|block-level display element]], so contiguous excerpts are normally separate block elements with line breaks between them. This can be overridden through use of param {{para|inline|yes}}, which suppresses the div-tag, and results in an [[HTML element#Inline elements|inline display element]] instead. In this case, just as with running text on adjacent lines of [[wikicode]], no line break is generated between them.
This technique can also be adapted to [[#Differing citation styles|§ change citation style]] or use different references.
=== Mitigating side effects of unexpected source file changes ===
{{anchor|HIDDENEXCERPTADVICE}}{{tsh|TM:HIDDENEXCERPTADVICE}}
Excerpting content from a source page section into a target may cause [[WP:Link|link rot]] and other undesirable effects in the target if the heading or source content changes unexpectedly. Editors of the source page are generally unaware that editing the source can have side effects elsewhere. This situation can be mitigated by adding a [[WP:Hidden|hidden text]] message at the source page section being excerpted:
: <code><nowiki><!-- Note: Content in this section is {{excerpt}}ed by "PageName#SectionName". See TM:HIDDENEXCERPTADVICE. --></nowiki></code>
This alerts editors of the source file that part or all of the content in the area below the message is excerpted onto another page, so the editor can take target page side effects into account during their edit. Changing the heading is analogous to [[MOS:HIDDENLINKADVICE]], which describes how to avoid undesirable side effects when a section heading change may lead to a broken redirect.
'''What to do'''
If you are about to edit a section that contains a hidden excerpt advice message, what should you do?
* If your prospective changes are typos, grammar, or wording adjustments '''without changing the section heading''', most likely you don't need to do anything
* If you are changing the section heading, then this will likely break the {{t|excerpt}} and leave an error message at the target page. Have a look at the indicated source page and find the {{t|excerpt}} template. If it contains a pointer to the old section heading name on the source page section (see [[Template:Excerpt#Parameters|positional parameter 2]]), then you should update that parameter to the new section name.
* If you are making a substantial change to excerpted content at the source in a way that may adversely affect the target page, you could:
*# raise a discussion at the target Talk page, to advise editors about changes to the source; you could link [[TM:HIDDENEXCERPTADVICE]] in the discussion or in the edit summary
*# replace the {{t|excerpt}} template in the target with content [[WP:CWW|copied]] from the version of the source before you changed it. (Don't forget to provide [[WP:CWW|copy attribution]] in the edit summary). This is a permanent solution for that excerpt, and you should remove the hidden link from the source page after completing it.
== Advantages and disadvantages ==
The use of {{tl|Excerpt}} has the following advantages:
* '''Reduces maintenance''' by avoiding duplicate content that must be updated multiple times
* '''Improves content quality''' by encouraging editors to merge related content, rather than having multiple versions in various stages of development (see [[#Replacing summary section with excerpt of child article]])
* '''Fosters collaboration''' by channeling contributors into one place, rather than working in parallel
* '''Promotes rapid development''' of articles, especially those written in [[WP:Summary style|summary style]]
It also has the following disadvantages:
* '''Impediment to editing''' as you have to go to the sub article to make changes to the main article (though excerpts include a hatnote with an [edit] button to edit the excerpted article in one click)
* '''Reduces accuracy''' as an excerpt of one article is not always a perfect fit into a new article (but see [[#Refinement using inclusion control]])
* '''Decreases visibility''' as changes to the sub article will not appear on the watchlist of editors of the main article (see [[phab:T55525]])
* '''Risk of linkrot''' as pages or sections are blanked, moved, or deleted; this may result in the appearance of [[#Error messages|§ error messages]] on the page (but see {{slink||Mitigating side effects of unexpected source file changes}} above; additionally, broken excerpts are automatically tracked at [[:Category:Articles with broken excerpts]] and regularly fixed).
==Incompatibilities==
{{Replace | {{excerpt|Template:R/doc|Incompatibilities |hat=no}} | this template | template {{tl|R}} }}
== Error messages ==
If an error is detected, an error message will appear in the article in place of the expected transcluded content:
* <span class="error">No page given</span> – No page was passed to the template <!--error-no-page-->
* <span class="error">Title X is not valid</span> – The title passed is not valid (contains [[Wikipedia:Naming conventions (technical restrictions)#Forbidden characters|forbidden characters]] such as < or >) <!--error-invalid-title-->
* <span class="error">Page X not found</span> – The page passed does not exist, or the page is a redirect and the target page was not found <!--error-page-not-found-->
* <span class="error">Lead section is empty</span> – The page exists, but cannot excerpt from non-existent lead <!--error-lead-empty-->
* <span class="error">Section X not found</span> – The page exists, but cannot excerpt the desired section because either:
** The given section does not exist. <!--error-section-not-found--> This may occur if the source page section is removed or renamed. To help mitigate this, see [[MOS:BROKENSECTIONLINKS]].
** The given section exists, but is excluded from transclusion by one of the [[Help:Template#Inclusion control: noinclude, includeonly, and onlyinclude|inclusion control]] tags.
* <span class="error">Section X is empty</span> – The given section exists, but is empty <!--error-section-empty-->
* <span class="error">Template loop detected</span> – The excerpted section contains itself an excerpt. While this is not strictly a template loop, the software considers it so. To fix it, skip the intermediate excerpt ([[Special:Diff/1184292340|example]]).
== See also ==
* [[Module:Excerpt]]
* [[Module:Excerpt/config]]
* [[Module:WikitextParser]]
* [[c:Data:I18n/Module:Excerpt.tab]]
* [[Help:Transclusion#Drawbacks]]
* [[:Category:Articles with excerpts]], or [https://en.wikipedia.org/w/index.php?title=Special%3AWhatLinksHere&target=Template%3AExcerpt&namespace=0 Articles that link to "Template:Excerpt"] (unsorted)
* [[:Category:Articles with broken excerpts]]
* [[Help:Labeled section transclusion]] – A more efficient method for simple section transclusions
* [[Wikipedia:Transclusion#Selective transclusion]] – How to transclude one or more sections of an article or project page into another
* [[Wikipedia:Summary style#Synchronization]]
* [[Wikipedia:Manual of Style/Lead section]]
* [[Wikipedia:WikiProject Introductions]]
* {{tl|Transclude lead excerpt}} and {{tl|Transclude linked excerpt}} – Templates designed for excerpt transclusion in portals
* {{tl|Transcluded section}} – Creates hatnote but ''does not transclude'' the section
** [https://en.wikipedia.org/w/index.php?title=Special%3AWhatLinksHere&target=Template%3ATranscluded+section&namespace=0 Pages that link to "Template:Transcluded section" (articles)] (unsorted)
* {{tl|Transcluding article}} – Transcludes one or more entire pages
** [https://en.wikipedia.org/w/index.php?title=Special%3AWhatLinksHere&target=Template%3ATranscluding+article&namespace=0 Pages that link to "Template:Transcluding article" (articles)] (unsorted)
* {{tl|Template parameter value}} – Extracts the value of a parameter passed to a template
* [[meta:Grants:Project/Rapid/Sophivorus/Excerpts]] - Grant to spread excerpts to various Wikipedias
* [[meta:Concise Wikipedia]] - Perennial new project proposal (see the [[meta:Concise Wikipedia#A summary of existing short-options, using an example|comparison table]] at the bottom in particular)
* [[Single source of truth]]
* [[Wikimania:2021:Submissions/Excerpts: Modular and Reusable Content within Wikipedia|Excerpts: Modular and Reusable Content within Wikipedia]] - Video presentation about excerpts for Wikimania 2021
== Template data ==
<templatedata>
{
"params": {
"1": {
"aliases": [
"article",
"page"
],
"label": "Article",
"description": "Name of the article or page to transclude",
"example": "Science",
"type": "wiki-page-name",
"required": true
},
"2": {
"aliases": [
"section",
"fragment"
],
"label": "Section",
"description": "Name of the section or <section> tag to transclude",
"example": "History",
"type": "string"
},
"paragraphs": {
"label": "Paragraphs",
"description": "Paragraphs to transclude",
"example": "1-3,5",
"type": "string",
"aliases": [
"paragraph"
]
},
"files": {
"label": "Files",
"description": "Files to transclude",
"example": "1-3,5",
"type": "string",
"default": "1",
"aliases": [
"file"
]
},
"subsections": {
"label": "Subsections",
"description": "Whether to transclude the subsections of the requested section",
"example": "yes",
"type": "boolean"
},
"tables": {
"label": "Tables",
"description": "Tables to transclude",
"example": "Stats2020",
"type": "string",
"aliases": [
"table"
]
},
"references": {
"label": "References",
"description": "Whether to transclude the references",
"example": "no",
"type": "boolean"
},
"only": {
"label": "Only",
"description": "Transclude only this kind of element",
"example": "table",
"type": "string"
},
"this": {
"label": "This",
"description": "Change the initial text of the hatnote",
"example": "This gallery is",
"type": "string"
},
"displaytitle": {
"label": "Display title",
"description": "Change the text of the link in the hatnote",
"type": "string"
},
"class": {
"label": "Class",
"description": "Additional CSS class",
"example": "noprint",
"type": "string"
},
"hat": {
"label": "Hatnote",
"description": "Whether to include the hatnote",
"example": "no",
"type": "boolean",
"default": "yes"
},
"bold": {
"label": "Bold",
"description": "Whether to preserve bold text",
"example": "no",
"type": "boolean",
"default": "yes"
},
"links": {
"label": "Wikilinks",
"description": "Whether to preserve wikilinks",
"example": "no",
"type": "boolean",
"default": "yes"
},
"quote": {
"label": "Quote",
"description": "Wraps the excerpt in <blockquote> tags",
"example": "yes",
"type": "boolean",
"default": "no"
},
"inline": {
"label": "Inline",
"description": "Remove the hatnote and <div> tags around the excerpt, to use it inside other text",
"example": "yes",
"type": "boolean",
"default": "no"
},
"lists": {
"aliases": [
"list"
],
"label": "Lists",
"description": "Lists to transclude",
"example": "1",
"type": "string"
},
"templates": {
"aliases": [
"template"
],
"label": "Templates",
"description": "Templates to transclude",
"example": "Infobox person",
"type": "string"
},
"onlyfreefiles": {
"label": "Only free files",
"description": "Disable transclusion of non-free files",
"example": "no",
"type": "boolean",
"default": "yes"
},
"briefdates": {
"label": "Brief dates",
"description": "Abbreviate birth and death information to (YYYY-YYYY) format",
"example": "yes",
"type": "boolean",
"default": "no"
}
},
"description": "This template is used for transcluding part of an article into another article.",
"paramOrder": [
"1",
"2",
"only",
"paragraphs",
"files",
"tables",
"lists",
"templates",
"references",
"subsections",
"hat",
"bold",
"links",
"quote",
"this",
"displaytitle",
"inline",
"onlyfreefiles",
"briefdates",
"class"
]
}
</templatedata>
<includeonly>{{sandbox other||
<!-- Categories go below this line; interwikis go to Wikidata. -->
[[Category:Wikipedia page-section templates]]
[[Category:Transclude page content templates]]
}}</includeonly>
85nw1lji79re3n20seqao7iopnf39e1
797165
797164
2026-06-08T20:19:21Z
SM7
3953
1 revision imported from [[:en:Template:Excerpt/doc]]
797164
wikitext
text/x-wiki
{{High-use}}
{{Documentation subpage}}
{{never subst}}
{{Lua|Module:Excerpt}}
This template is used for reusing parts of pages in other pages. This practice has various [[#Advantages and disadvantages|advantages and disadvantages]].
This template extends the capabilities of the built-in [[Help:Transclusion|normal transclusion]] and [[Help:Labeled section transclusion|labeled section transclusion]].
== Usage ==
* <code><nowiki>{{Excerpt|Page title}}</nowiki></code> – Transclude the lead section ([[Africa#Architecture|example]])
* <code><nowiki>{{Excerpt|Page title|Section title}}</nowiki></code> – Transclude a specific section, excluding any subsections ([[Eating#Mammals|example]])
== Parameters ==
There is one required parameter, and numerous optional ones for configuring the excerpt:
=== Summary ===
'''Source identification'''
* {{para|1}} – Name of the article or page to transclude. '''Required.''' Aliases: {{para|article}} or {{para|page}}.
* {{para|2}} – Name of the section or tag to transclude. Optional; if omitted, transcludes the lead section (content above the first section header). Aliases: {{para|section}} or {{para|fragment}}.
'''Transclusion config'''
Transcludable content is defined as one of several ''element types'': {{pval|file}}, {{pval|list}}, {{pval|paragraph}}, {{pval|reference}}, {{pval|subsection}}, {{pval|table}}, or {{pval|template}}.
Config parameters specify which ''element type'' to transclude, and in some cases, ''how many'' and ''which'' items of that type to transclude. All config parameters are optional; if omitted, all items of all element types are transcluded from the source page identified by the two unnamed parameters. Some element types support conditional item transclusion by specifying an item number range (1-3) or comma series (1, 2, 5); these types include: files, lists, paragraphs, and tables.
There are ten optional transclusion configuration parameters:
* {{para|only}} – Element types to transclude. Values: {{pval|file(s)}}, {{pval|list(s)}}, {{pval|table(s)}}, {{pval|template(s)}}, {{pval|paragraph(s)}}. Default: all element types.
* {{para|files}} – [[WP:FILE|Files]] to transclude. Default: all files. Same basic syntax as {{para|paragraphs}}, but see {{slink||Details}}.
** {{para|onlyfreefiles|no}} – Enables transclusion of [[WP:Non-free content|non-free files]]. Default: exclude non-free content.
* {{para|lists}} – Lists ([[MOS:LIST#Bulleted lists|bulleted]], [[MOS:LIST#Numbered lists|numbered]]) to transclude. Default: all lists. Same syntax as for {{para|paragraphs}}.
* {{para|paragraphs}} – [[H:PARAGRAPH|Paragraphs]] to transclude. Default: all paragraphs.
* {{para|references|no}} – Exclude all [[WP:REF|references]] between <code><nowiki><ref>...</ref></nowiki></code> tags.
* {{para|subsections|yes}} – Include [[MOS:SECTIONS|subsections]] of the transcluded section. Default: only content above the first subsection header.
* {{para|tables}} – [[Help:Table|Tables]] to transclude. Default: all tables. Same basic syntax as {{para|paragraphs}}, but see {{slink||Details}}.
* {{para|templates}} – [[Help:Template|Templates]] to transclude. By default all templates are transcluded, except those blacklisted at [[Module:Excerpt/config]]. See {{slink||Details}} for how to specify a specific template or templates for inclusion or exclusion.
'''Style and extra features'''
These optional parameters alter the way transcluded items are displayed:
* {{para|bold|yes}} – Preserve '''bold''' text.
* {{para|briefdates|yes}} – Abbreviate birth and death information to (YYYY-YYYY) format
* {{para|displaytitle}} – Change the text of the link in the hatnote. For example, [[WP:ITHAT|add italics]], subscripts, etc.
* {{para|hat|no}} – Hide the [[Wikipedia:Hatnote|hatnote]] "This section is an excerpt from..."
* {{para|inline|yes}} – Remove the <code><nowiki><div></nowiki></code> tags around the excerpt, to use it inside other text, or to add references or other content after it [[#Suppress line breaks between paragraphs|with no paragraph break]] between them. Also hides the hatnote; to reestablish it, use {{tl|transclusion notice}} above the Excerpt invocation.
* {{para|links|no}} – Unlink all [[H:WIKILINK|wikilinks]] and render as plain text.
* {{para|quote|yes}} – Wrap the excerpt with <code><nowiki><blockquote></nowiki></code> tags.
* {{para|this}} – Change the initial text of the hatnote. For example, if the transcluded content is a gallery, you can set {{para|this|This gallery is}} so that the hatnote reads "This gallery is an excerpt from..."
=== Details ===
* {{para|1}} or {{para|article}} or {{para|page}}
*: By default the lead section is transcluded ([[Africa#Water|example]]). If the page contains an infobox, the image and caption of the infobox will be transcluded (unless {{para|files|0}} is set). Also, templates listed at [[Module:Excerpt/config]] will not be transcluded (unless requested explicitly with {{para|templates}}, see below).
* {{para|2}} or {{para|section}} or {{para|fragment}}
*: Name of the section to transclude ([[Eating#Mammals|example]]) or of the <code><nowiki><section></nowiki></code> tag to transclude ([[Axiom Space#Ax-1|example]]). In the case of a section tag, the section must be marked with <code><nowiki><section begin="Name of the fragment" /></nowiki></code> and <code><nowiki><section end="Name of the fragment" /></nowiki></code> in the page to be transcluded. Notice that this template provides other ways of targeting specific fragments of a page without having to resort to section tags.
* {{para|only}}
*: The ''element type'' to transclude, excluding all other types. By default all element types are transcluded. Param {{para|only}} is an exclusionary param, and excludes all other types of elements, except the one you name, so that for example, specifying {{para|only|paragraphs}} excludes all lists, tables, templates, and so on. Param values can be in the singular (e.g., {{para|only|paragraph}}) or plural (e.g., {{para|only|paragraphs}}) and mean different things: in the singular, only the first item of that element type is transcluded; in the plural, all items are.
*:* {{para|only|file}} – Transclude only the first file (but no lists, paragraphs, tables, etc.)
*:* {{para|only|files}} – Transclude all files (but nothing else)
*:* {{para|only|filename}} – Transclude only the file name (Example.jpg) of the first file (and nothing else). If no file is found, "Noimage.png" will be returned.
*:* {{para|only|list}} – Transclude only the first list, exclude all other element types
*:* {{para|only|lists}} – Transclude all lists (but nothing else)
*:* {{para|only|table}} – Transclude only the first table, exclude all other element types. Does not transclude table templates, use {{para|templates}} for that.
*:* {{para|only|tables}} – Transclude all tables (but nothing else) ([[JKT48#Singles|example]])
*:* {{para|only|template}} – Transclude only the first template (excluding templates blacklisted at [[Module:Excerpt/config]], as well as all other element types)
*:* {{para|only|templates}} – Transclude all templates (excluding blacklisted templates) (but nothing else)
*:* {{para|only|paragraph}} – Transclude only the first paragraph, exclude all other element types
*:* {{para|only|paragraphs}} – Transclude all paragraphs (but nothing else)
* {{para|files}}
*: An ''element item'' param, defining which files, such as images and other media, to transclude. Default: all files.
*:* {{para|files|0}} – Transclude no files
*:* {{para|files|A.jpg}} – Transclude the file named 'A.jpg'
*:* {{para|files|A.jpg, B.png, C.gif}} – Transclude the files named 'A.jpg', 'B.png' and 'C.gif'
*:* {{para|files|.+%.png}} – Transclude all PNG files
*:* {{para|files|-A.jpg}} – Transclude all files except the one named 'A.jpg'
*:* {{para|files|-A.jpg, B.png, C.gif}} – Transclude all files except the ones named 'A.jpg', 'B.png' and 'C.gif'
*:* {{para|files|-.+%.png}} – Transclude all non-PNG files
* {{para|paragraphs}}
*: An ''element item'' param, defining which paragraphs to transclude. By default all paragraphs are transcluded.
*:* {{para|paragraphs|0}} – Transclude no paragraphs
*:* {{para|paragraphs|1}} – Transclude the first paragraph
*:* {{para|paragraphs|2}} – Transclude the second paragraph
*:* {{para|paragraphs|1,3}} – Transclude the first and third paragraphs
*:* {{para|paragraphs|1-3}} – Transclude the first, second and third paragraphs
*:* {{para|paragraphs|1-3,5}} – Transclude the first, second, third and fifth paragraphs
*:* {{para|paragraphs|-1}} – Transclude all paragraphs except the first
*:* {{para|paragraphs|-2}} – Transclude all paragraphs except the second
*:* {{para|paragraphs|-1,3}} – Transclude all paragraphs except the first and third
*:* {{para|paragraphs|-1-3}} – Transclude all paragraphs except the first, second and third
*:* {{para|paragraphs|-1-3,5}} – Transclude all paragraphs except the first, second, third and fifth
* {{para|subsections|yes}}
*: An ''element item'' param, defining which subsections of the source to transclude. Default: only the portion of the source lying above the first subsection header. Notice that if the transclusion is done from a section level 3 in the transcluding page, and the transcluded subsections are also level 3, then transcluded subsections will show with the same hierarchy as the transcluding section, which may not be the desired outcome, so use with caution.
* {{para|tables}}
*: An ''element item'' param, defining which tables to transclude. Default: all tables. Does not transclude table templates just above the tables. Same syntax as when transcluding paragraphs, but also:
*:* {{para|tables|Stats2020}} – Transclude the table with id 'Stats2020'
*:* {{para|tables|Stats2020, Stats2019, Stats2018}} – Transclude the tables with ids 'Stats2020', 'Stats2019' and 'Stats2018'
*:* {{para|tables|-Stats2020}} – Transclude all tables except the one with id 'Stats2020'
*:* {{para|tables|-Stats2020, Stats2019, Stats2018}} – Transclude all tables except the ones with ids 'Stats2020', 'Stats2019' and 'Stats2018'
* {{para|templates}}
*: An ''element item'' param, defining which templates to transclude. Default: all templates except those blacklisted at [[Module:Excerpt/config]]. Using a hyphen (minus sign) before a comma-delimited list of templates excludes those templates from transclusion.
*:* {{para|templates|-Ocean}} – Add the template 'Ocean' to the blacklist
*:* {{para|templates|-Ocean, Nature}} – Add the templates 'Ocean' and 'Nature' to the blacklist
*:* {{para|templates|Infobox person}} – Ignore the blacklist and transclude the template 'Infobox person'
*:* {{para|templates|Infobox person, Ocean}} – Ignore the blacklist and transclude the templates 'Infobox person' and 'Ocean'
*:* {{para|templates|.*}} – Ignore the blacklist and transclude all templates
== Tips and how-to ==
=== Compared to #section ===
{{Further|Help:Transclusion#Standard section transclusion}}
For simple cases of transcluding sections of articles, the {{pf|section}}, {{pf|section-x}}, and {{pf|section-h}} (abbreviated {{pf|lst}}, {{pf|lstx}}, and {{pf|lsth}})) parser functions can be used instead of this template. {{pf|lsth|''article''|''fragmentname''}} will transclude the section of "''article''" with the header "''fragmentname''", and {{pf|lsth|''article''}} will transclude the lead section of "''article''". Excerpting only specific paragraphs can be done by marking up the source article with <code><nowiki><section begin=</nowiki>''<nowiki>fragmentname</nowiki>''<nowiki>/>...<section end=</nowiki>''<nowiki>fragmentname</nowiki>''<nowiki>/></nowiki></code> tags and using {{pf|lst|''article''|''fragmentname''}} to transclude those fragments, which is equivalent to using the {{para|fragment|''fragmentname''}} parameter with this template. {{pf|lsth|''article''|''fragmentname''}} can also be used to transclude everything ''but'' those fragments.
The text will not be trimmed of excess whitespace, there will not be a header (equivalent to {{para|hat|no}}), and all files, templates, tables, references, and subsections will be included unless the source article is marked up with <code><nowiki><section begin=</nowiki>''<nowiki>fragmentname</nowiki>''<nowiki>/>...<section end=</nowiki>''<nowiki>fragmentname</nowiki>''<nowiki>/></nowiki></code>, {{tag|noinclude}}, or {{tag|onlyinclude}} tags. [[Help:Self link|Self links]] will appear in bold.
=== Citations ===
==== Differing styles ====
{{Further|Wikipedia:Citing sources#Variation in citation methods}}
It can happen that the source you want to excerpt contains footnotes in a [[WP:CITEVAR|different citation style]] than your article, and excerpting the source would cause a citation style mismatch, which is contrary to the guideline on [[Wikipedia:Citing sources|citing sources]]. Sometimes, excerpt can still be used, while avoiding a mismatch in style, by the use of params {{para|references|no}} and {{para|inline|yes}}.
If the source you want to excerpt has multiple ref-tags interspersed throughout the source, and they need to display exactly in those locations in order to maintain full [[WP:V|verifiability]], then this source might not be a good candidate for transclusion via the {{tl|excerpt}} template, and [[WP:CWW|copying the content]] from the source into the article might be a better choice.
However, you can still use the Excerpt template, if the source page you want to excerpt meets '''either''' of these conditions:
* references are found mostly or all at the end of the text to be excerpted; or
* references are scattered throughout, but could legitimately be regrouped at the end of the excerpt without adversely affecting [[WP:V|verifiability]].
In either case, use params {{para|references|no}} to strip the ref-tags from the transcluded content, and {{para|inline|yes}} to define the excerpt as an [[HTML element#Inline elements|inline display element]] in order to {{slink||Suppress line breaks between paragraphs}}, and then manually append a copy of all the citations in the source immediately after the excerpt tag ending curly braces in the target article, with no intervening line breaks, white space, or other characters between the tag and the appended references. The copied references will have to be manually converted from [[Wikipedia:Citing sources#Short and full citations|short footnote-style to full, inline citation-style]], or vice-versa, to match the citation style of the target. '''Note:''' citations are not creative content, and [[Wikipedia:Copying within Wikipedia#Where attribution is not needed|attribution is not needed]] for copying them.
==== Another approach ====
It is advised to seek consensus before attempting this layout change.
If there are few shortened footnotes, a new subsection can be added to the "References" section to allow {{tl|sfn}} to link to the correct citations. Here is one approach, where the {{tl|sfn}} citations list in the excerpted article "SCBA" is titled "Works cited":
<syntaxhighlight lang="wikitext" highlight="7-8">
== Transcluded section ==
{{excerpt|SCBA}}
== References ==
{{reflist}}
=== From SCBA ===
{{excerpt|SCBA|Works cited|hat=0}}
</syntaxhighlight>
{{tl|sfn}} footnotes will now link to the citations created in the excerpted subsection.
==== Ref name collision ====
To prevent the possibility of collisions between [[WP:NAMEDREFS|named references]] in the transcluding and excerpted pages which would otherwise generate a [[Help:Cite errors/Cite error references duplicate key|duplicate key error]], all ref names in the excerpted content are altered by prefixing the name with the pagename of the excerpted page. Thus, if the ref on page {{kbd|MyArticle}} looks like <code><nowiki><ref name="Jones-2024"></nowiki></code>, then when excerpted by {{kbd|TargetPage}} it will be changed to <code><nowiki><ref name="MyArticle Jones-2024"></nowiki></code>, in order to avoid a collision with a possible "Jones-2024" ref already on the target page.
=== Excerpt trees ===
[[File:Excerpt tree.png|class=skin-invert-image|thumb|Visual representation of an imaginary excerpt tree.]]
When a very general article uses excerpts from more specific articles, which in turn use excerpts from even more specific articles, then a [[tree structure]] emerges, called an "Excerpt tree".
Here you can navigate the main excerpt trees on the English Wikipedia. It's useful for editors interested in expanding or improving them. To navigate the trees, click the following button<sup><abbr title="{{Int:gadgets-sister}}">(S)</abbr></sup>:
{{Clickable button|url={{fullurl:{{FULLPAGENAME}}#Excerpt trees|withJS=MediaWiki:ExcerptTree.js}} |2=See the excerpt trees}}
<div class="ExcerptTree" style="display: none;">
* [[January 6 United States Capitol attack]]
* [[Algae]]
* [[Africa]]
* [[Aquatic ecosystem]]
* [[Bangladesh]]
* [[Campanology]]
* [[Climate change in Africa]]
* [[Climate change in South Asia]]
* [[Climate justice]]
* [[COVID-19 pandemic]]
* [[Ecosystem]]
** [[Aquatic ecosystem]]
* [[Effects of climate change on humans]]
* [[Environmental impact of agriculture]]
* [[Eutrophication]]
* [[Fishing industry]]
* [[Food]]
* [[God]]
** [[Existence of God]]
* [[Hygiene]]
* [[List of carillons]]
* [[List of LGBT-related cases in the United States Supreme Court]]
* [[Marine debris]]
* [[Middle East]]
* [[Ocean]]
* [[Plastic pollution]]
* [[Public health]]
* [[Sanitation]]
* [[Sewage]]
* [[Sustainable Development Goals]]
* [[Tourism]]
* [[Wastewater treatment]]
* [[Water resources]]
</div>
=== Refinement using inclusion control ===
{{Further|Help:Template#Noinclude, includeonly, and onlyinclude}}
Sometimes, a passage will almost fit for a transclusion, but not quite. In these cases, you can edit the source page to add <code><nowiki><noinclude>...</noinclude></nowiki></code> tags around content you don't want in the excerpt and <code><nowiki><includeonly>...</includeonly></nowiki></code> tags around content you want only in the excerpt.
For instance, the page [[COVID-19 misinformation]] begins with "The [[COVID-19 pandemic]] has resulted in [[misinformation]]...". However, when excerpting this lead to the misinformation section of [[COVID-19 pandemic]], we don't need to specify which pandemic we're referring to. Therefore, the code <code><nowiki>The <noinclude>[[COVID-19 pandemic]]</noinclude><includeonly>pandemic</includeonly> has resulted in [[misinformation]]</nowiki></code> can be used at the misinformation page, so that it will appear at the pandemic page as "The pandemic has resulted in [[misinformation]]...".
For pages with a high volume of edits, it may be a good idea to leave a hidden comment explaining why the tags are there, so that no one will be [[WP:FENCE|tempted]] to remove them, like so: <code><nowiki>The <noinclude>[[COVID-19 pandemic]]</noinclude><!--These tags are used to refine the excerpt at [[COVID-19 pandemic]]--><includeonly>pandemic</includeonly> has resulted in [[misinformation]]</nowiki></code>
Please note that when the <code><nowiki></noinclude></nowiki></code> tag is wrapped into a new line, a character next to it would be interpreted as a line beginning. This can lead to some formatting problems. For example, when a <code><nowiki></noinclude></nowiki></code> at line beginning is succeeded by a [[whitespace character]], the page engine would translate this as a [[Help:Wikitext#Limiting formatting / escaping wiki markup|leading space]] that renders the subsequent paragraph in [[code block]] and [[monospaced font]] with preserved formatting. For this reason, no spaces should separate the <code><nowiki></noinclude></nowiki></code> tag from the text it precedes.
=== Replacing summary section with excerpt of child article ===
[[File:How to excerpt.webm|thumb|How to replace a section with an excerpt.]]
A section is often a [[WP:Summary style|summary]] in a [[WP:G#Parent article|parent article]] of a more detailed page about a subtopic located in a [[WP:G#child page|child page]]; these are generally linked with [[Template:Main]] in the parent. Sometimes it's convenient to replace the content of such a summary section in the parent with an excerpt of the child page lead (after merging any valuable content of the section into the child page). In such cases, an efficient way to proceed is:
# Open the parent section for editing in one tab, and the child article in another.
# Copy the text of the parent section and append it to the lead section of the child page.
# Consolidate and adjust the combined lead using common sense.
# Save the changes in the child article with an [[WP:ES|edit summary]] like: "{{xt|<nowiki>Copied content from [[Page]]. See that article's history for attribution."</nowiki>}}
# Back in the parent page section, delete all content except the section header and replace it with an excerpt of the child page.
# Save the changes in the section; proposed edit summary: "{{xt|Moved section content to <nowiki>[[Child page title]]</nowiki> and replaced with excerpt.}}"
'''IMPORTANT!''' In step four, include the '''full''' edit summary as shown to comply with Wikipedia's [[Wikipedia:Copying within Wikipedia|copyright policy]]. If you forget to do this at the time of the original edit, follow the instructions on [[Wikipedia:Copying within Wikipedia#Repairing insufficient attribution|Repairing insufficient attribution]] to create a dummy edit with the required edit summary.
=== Suppress line breaks between paragraphs ===
If you want to merge two excerpted paragraphs from a source into one longer one in your article, use two excerpts instead of one, and change the display mode to [[HTML element#Inline elements|inline]]. So, for example, instead of this :
: {{tlc|excerpt|Ocean color|paragraphs{{=}}2-3|file{{=}}no}} // (example taken from [[Ocean#Color]])
you could code:
: {{tlc|excerpt|Ocean color|paragraphs{{=}}2|file{{=}}no|inline{{=}}yes}}
: {{tlc|excerpt|Ocean color|paragraphs{{=}}3|file{{=}}no|inline{{=}}yes}}
and this will remove the [[Help:Line-break handling#Newlines|line break]] between the two paragraphs, so they will render as one paragraph.
By default, an {{tl|excerpt}} generates an HTML [[div and span#Differences and default behaviour|div-tag]], which is a [[HTML element#Block elements|block-level display element]], so contiguous excerpts are normally separate block elements with line breaks between them. This can be overridden through use of param {{para|inline|yes}}, which suppresses the div-tag, and results in an [[HTML element#Inline elements|inline display element]] instead. In this case, just as with running text on adjacent lines of [[wikicode]], no line break is generated between them.
This technique can also be adapted to [[#Differing citation styles|§ change citation style]] or use different references.
=== Mitigating side effects of unexpected source file changes ===
{{anchor|HIDDENEXCERPTADVICE}}{{tsh|TM:HIDDENEXCERPTADVICE}}
Excerpting content from a source page section into a target may cause [[WP:Link|link rot]] and other undesirable effects in the target if the heading or source content changes unexpectedly. Editors of the source page are generally unaware that editing the source can have side effects elsewhere. This situation can be mitigated by adding a [[WP:Hidden|hidden text]] message at the source page section being excerpted:
: <code><nowiki><!-- Note: Content in this section is {{excerpt}}ed by "PageName#SectionName". See TM:HIDDENEXCERPTADVICE. --></nowiki></code>
This alerts editors of the source file that part or all of the content in the area below the message is excerpted onto another page, so the editor can take target page side effects into account during their edit. Changing the heading is analogous to [[MOS:HIDDENLINKADVICE]], which describes how to avoid undesirable side effects when a section heading change may lead to a broken redirect.
'''What to do'''
If you are about to edit a section that contains a hidden excerpt advice message, what should you do?
* If your prospective changes are typos, grammar, or wording adjustments '''without changing the section heading''', most likely you don't need to do anything
* If you are changing the section heading, then this will likely break the {{t|excerpt}} and leave an error message at the target page. Have a look at the indicated source page and find the {{t|excerpt}} template. If it contains a pointer to the old section heading name on the source page section (see [[Template:Excerpt#Parameters|positional parameter 2]]), then you should update that parameter to the new section name.
* If you are making a substantial change to excerpted content at the source in a way that may adversely affect the target page, you could:
*# raise a discussion at the target Talk page, to advise editors about changes to the source; you could link [[TM:HIDDENEXCERPTADVICE]] in the discussion or in the edit summary
*# replace the {{t|excerpt}} template in the target with content [[WP:CWW|copied]] from the version of the source before you changed it. (Don't forget to provide [[WP:CWW|copy attribution]] in the edit summary). This is a permanent solution for that excerpt, and you should remove the hidden link from the source page after completing it.
== Advantages and disadvantages ==
The use of {{tl|Excerpt}} has the following advantages:
* '''Reduces maintenance''' by avoiding duplicate content that must be updated multiple times
* '''Improves content quality''' by encouraging editors to merge related content, rather than having multiple versions in various stages of development (see [[#Replacing summary section with excerpt of child article]])
* '''Fosters collaboration''' by channeling contributors into one place, rather than working in parallel
* '''Promotes rapid development''' of articles, especially those written in [[WP:Summary style|summary style]]
It also has the following disadvantages:
* '''Impediment to editing''' as you have to go to the sub article to make changes to the main article (though excerpts include a hatnote with an [edit] button to edit the excerpted article in one click)
* '''Reduces accuracy''' as an excerpt of one article is not always a perfect fit into a new article (but see [[#Refinement using inclusion control]])
* '''Decreases visibility''' as changes to the sub article will not appear on the watchlist of editors of the main article (see [[phab:T55525]])
* '''Risk of linkrot''' as pages or sections are blanked, moved, or deleted; this may result in the appearance of [[#Error messages|§ error messages]] on the page (but see {{slink||Mitigating side effects of unexpected source file changes}} above; additionally, broken excerpts are automatically tracked at [[:Category:Articles with broken excerpts]] and regularly fixed).
==Incompatibilities==
{{Replace | {{excerpt|Template:R/doc|Incompatibilities |hat=no}} | this template | template {{tl|R}} }}
== Error messages ==
If an error is detected, an error message will appear in the article in place of the expected transcluded content:
* <span class="error">No page given</span> – No page was passed to the template <!--error-no-page-->
* <span class="error">Title X is not valid</span> – The title passed is not valid (contains [[Wikipedia:Naming conventions (technical restrictions)#Forbidden characters|forbidden characters]] such as < or >) <!--error-invalid-title-->
* <span class="error">Page X not found</span> – The page passed does not exist, or the page is a redirect and the target page was not found <!--error-page-not-found-->
* <span class="error">Lead section is empty</span> – The page exists, but cannot excerpt from non-existent lead <!--error-lead-empty-->
* <span class="error">Section X not found</span> – The page exists, but cannot excerpt the desired section because either:
** The given section does not exist. <!--error-section-not-found--> This may occur if the source page section is removed or renamed. To help mitigate this, see [[MOS:BROKENSECTIONLINKS]].
** The given section exists, but is excluded from transclusion by one of the [[Help:Template#Inclusion control: noinclude, includeonly, and onlyinclude|inclusion control]] tags.
* <span class="error">Section X is empty</span> – The given section exists, but is empty <!--error-section-empty-->
* <span class="error">Template loop detected</span> – The excerpted section contains itself an excerpt. While this is not strictly a template loop, the software considers it so. To fix it, skip the intermediate excerpt ([[Special:Diff/1184292340|example]]).
== See also ==
* [[Module:Excerpt]]
* [[Module:Excerpt/config]]
* [[Module:WikitextParser]]
* [[c:Data:I18n/Module:Excerpt.tab]]
* [[Help:Transclusion#Drawbacks]]
* [[:Category:Articles with excerpts]], or [https://en.wikipedia.org/w/index.php?title=Special%3AWhatLinksHere&target=Template%3AExcerpt&namespace=0 Articles that link to "Template:Excerpt"] (unsorted)
* [[:Category:Articles with broken excerpts]]
* [[Help:Labeled section transclusion]] – A more efficient method for simple section transclusions
* [[Wikipedia:Transclusion#Selective transclusion]] – How to transclude one or more sections of an article or project page into another
* [[Wikipedia:Summary style#Synchronization]]
* [[Wikipedia:Manual of Style/Lead section]]
* [[Wikipedia:WikiProject Introductions]]
* {{tl|Transclude lead excerpt}} and {{tl|Transclude linked excerpt}} – Templates designed for excerpt transclusion in portals
* {{tl|Transcluded section}} – Creates hatnote but ''does not transclude'' the section
** [https://en.wikipedia.org/w/index.php?title=Special%3AWhatLinksHere&target=Template%3ATranscluded+section&namespace=0 Pages that link to "Template:Transcluded section" (articles)] (unsorted)
* {{tl|Transcluding article}} – Transcludes one or more entire pages
** [https://en.wikipedia.org/w/index.php?title=Special%3AWhatLinksHere&target=Template%3ATranscluding+article&namespace=0 Pages that link to "Template:Transcluding article" (articles)] (unsorted)
* {{tl|Template parameter value}} – Extracts the value of a parameter passed to a template
* [[meta:Grants:Project/Rapid/Sophivorus/Excerpts]] - Grant to spread excerpts to various Wikipedias
* [[meta:Concise Wikipedia]] - Perennial new project proposal (see the [[meta:Concise Wikipedia#A summary of existing short-options, using an example|comparison table]] at the bottom in particular)
* [[Single source of truth]]
* [[Wikimania:2021:Submissions/Excerpts: Modular and Reusable Content within Wikipedia|Excerpts: Modular and Reusable Content within Wikipedia]] - Video presentation about excerpts for Wikimania 2021
== Template data ==
<templatedata>
{
"params": {
"1": {
"aliases": [
"article",
"page"
],
"label": "Article",
"description": "Name of the article or page to transclude",
"example": "Science",
"type": "wiki-page-name",
"required": true
},
"2": {
"aliases": [
"section",
"fragment"
],
"label": "Section",
"description": "Name of the section or <section> tag to transclude",
"example": "History",
"type": "string"
},
"paragraphs": {
"label": "Paragraphs",
"description": "Paragraphs to transclude",
"example": "1-3,5",
"type": "string",
"aliases": [
"paragraph"
]
},
"files": {
"label": "Files",
"description": "Files to transclude",
"example": "1-3,5",
"type": "string",
"default": "1",
"aliases": [
"file"
]
},
"subsections": {
"label": "Subsections",
"description": "Whether to transclude the subsections of the requested section",
"example": "yes",
"type": "boolean"
},
"tables": {
"label": "Tables",
"description": "Tables to transclude",
"example": "Stats2020",
"type": "string",
"aliases": [
"table"
]
},
"references": {
"label": "References",
"description": "Whether to transclude the references",
"example": "no",
"type": "boolean"
},
"only": {
"label": "Only",
"description": "Transclude only this kind of element",
"example": "table",
"type": "string"
},
"this": {
"label": "This",
"description": "Change the initial text of the hatnote",
"example": "This gallery is",
"type": "string"
},
"displaytitle": {
"label": "Display title",
"description": "Change the text of the link in the hatnote",
"type": "string"
},
"class": {
"label": "Class",
"description": "Additional CSS class",
"example": "noprint",
"type": "string"
},
"hat": {
"label": "Hatnote",
"description": "Whether to include the hatnote",
"example": "no",
"type": "boolean",
"default": "yes"
},
"bold": {
"label": "Bold",
"description": "Whether to preserve bold text",
"example": "no",
"type": "boolean",
"default": "yes"
},
"links": {
"label": "Wikilinks",
"description": "Whether to preserve wikilinks",
"example": "no",
"type": "boolean",
"default": "yes"
},
"quote": {
"label": "Quote",
"description": "Wraps the excerpt in <blockquote> tags",
"example": "yes",
"type": "boolean",
"default": "no"
},
"inline": {
"label": "Inline",
"description": "Remove the hatnote and <div> tags around the excerpt, to use it inside other text",
"example": "yes",
"type": "boolean",
"default": "no"
},
"lists": {
"aliases": [
"list"
],
"label": "Lists",
"description": "Lists to transclude",
"example": "1",
"type": "string"
},
"templates": {
"aliases": [
"template"
],
"label": "Templates",
"description": "Templates to transclude",
"example": "Infobox person",
"type": "string"
},
"onlyfreefiles": {
"label": "Only free files",
"description": "Disable transclusion of non-free files",
"example": "no",
"type": "boolean",
"default": "yes"
},
"briefdates": {
"label": "Brief dates",
"description": "Abbreviate birth and death information to (YYYY-YYYY) format",
"example": "yes",
"type": "boolean",
"default": "no"
}
},
"description": "This template is used for transcluding part of an article into another article.",
"paramOrder": [
"1",
"2",
"only",
"paragraphs",
"files",
"tables",
"lists",
"templates",
"references",
"subsections",
"hat",
"bold",
"links",
"quote",
"this",
"displaytitle",
"inline",
"onlyfreefiles",
"briefdates",
"class"
]
}
</templatedata>
<includeonly>{{sandbox other||
<!-- Categories go below this line; interwikis go to Wikidata. -->
[[Category:Wikipedia page-section templates]]
[[Category:Transclude page content templates]]
}}</includeonly>
85nw1lji79re3n20seqao7iopnf39e1
शिक्षण बिधि
0
101019
797166
2026-06-08T20:23:42Z
SM7
3953
नया आधार लेख
797166
wikitext
text/x-wiki
'''शिक्षण विधि''' भा '''पढ़ावे के तरीका''' ({{Langx|en|Teaching Method}}) सिद्धांत आ तरीका सभ के एगो समूह हवे, जवना के इस्तेमाल टीचर विद्यार्थी के सीखला में मदद करे खातिर करेलन। ई रणनीति सभ कुछ हद तक पढ़ावल जाए वाला विषय, कुछ हद तक विद्यार्थी के ज्ञान आ दक्षता, आ कुछ हद तक सीखला के वातावरण से पैदा होखे वाली सीमाजन के आधार पर तय होले। कवनो शिक्षण विधि तबे उपयुक्त आ प्रभावी मानल जाले जब ऊ विद्यार्थी, विषयवस्तु के प्रकृति आ अपेक्षित सीखला के परिणाम के ध्यान में रखे।
शिक्षण के तरीका के मोटे तौर पर शिक्षक-केंद्रित आ विद्यार्थी-केंद्रित दृष्टिकोण में बाँटल जा सकेला। हालाँकि व्यवहार में शिक्षक अक्सर विद्यार्थी के पूर्व ज्ञान, अनुभव आ सीखला के लक्ष्य के अनुसार एह दुनो तरीका के बीच संतुलन बनावत रहेलन। शिक्षक-केंद्रित शिक्षण में शिक्षक मुख्य अधिकार प्राप्त व्यक्ति होलें। एह मॉडल में विद्यार्थी के अक्सर अइसन व्यक्ति के रूप में देखल जाला जे मुख्य रूप से व्याख्यान आ प्रत्यक्ष निर्देश के माध्यम से जानकारी ग्रहण करेला। एह तरीका में शिक्षक के प्रमुख भूमिका ज्ञान आ सूचना के हस्तांतरण कइल होला। शिक्षण आ मूल्यांकन के अलग-अलग प्रक्रिया मानल जाला, आ विद्यार्थी के उपलब्धि के माप आमतौर पर लिखित परीक्षा आ वस्तुनिष्ठ मूल्यांकन के माध्यम से कइल जाला। एकरा विपरीत, विद्यार्थी-केंद्रित शिक्षण में शिक्षक आ विद्यार्थी दुनो सीखला के प्रक्रिया में सक्रिय भूमिका निभावेलन। एह तरीका के कई बेर अधिकारपूर्ण (ऑथोरिटेटिव) दृष्टिकोण भी कहल जाला। एहमें शिक्षक के मुख्य काम मार्गदर्शक आ सहायक के रूप में विद्यार्थी के सीखला आ समझ विकसित करे में मदद कइल होला। विद्यार्थी के प्रगति के मूल्यांकन औपचारिक आ अनौपचारिक दुनो माध्यम से कइल जाला, जइसे समूह परियोजना, विद्यार्थी पोर्टफोलियो आ कक्षा में सहभागिता। एह मॉडल में शिक्षण आ मूल्यांकन एक-दूसरा से जुड़ल रहेला, आ शिक्षक पढ़ावे के दौरान लगातार विद्यार्थी के सीखला के आकलन करत रहेलन।
{{edu-stub}}
cju2j07009t6gvj4wmrjeewm3tw1jfa
Module:Excerpt
828
101020
797168
2018-04-22T16:45:02Z
en>Certes
0
Module to extract the lead from an article. Unlike #lsth, it removes infoboxes, hatnotes, etc.
797168
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename)
-- Find the lead section of the named page
-- %b{} removes any leading
-- If the page exists, protected or not, this is some other value
-- Note: this check does NOT record a wikilink or transclusion from the calling page to pagename
title=mw.title.new(pagename)
text=title.getContent(title)
text=mw.ustring.gsub(text,"%c%s*==.*","") -- remove first heading and everything after it
text=mw.ustring.gsub(text,"<noinclude>.-</noinclude>","") -- remove noinclude bits
text=mw.ustring.gsub(text,"^%A*%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
text=mw.ustring.gsub(text,"<ref.->.-</ref>","") -- remove refs
text=mw.ustring.match(text,"%C*'''.*") or text -- start at the first line with bold text, if any
return text;
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
return frame:preprocess(p._lead(args[1] or pargs[1]));
end
return p
32s80hptyxfu9p5f59kftzemtxmexdi
797169
797168
2018-04-22T17:14:41Z
en>Certes
0
Fix comments; return blank string on certain errors
797169
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename)
if not pagename then return "" end
title=mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
text=title.getContent(title) or ""
text=mw.ustring.gsub(text,"%c%s*==.*","") -- remove first heading and everything after it
text=mw.ustring.gsub(text,"<noinclude>.-</noinclude>","") -- remove noinclude bits
text=mw.ustring.gsub(text,"^%A*%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
text=mw.ustring.gsub(text,"<ref.->.-</ref>","") -- remove refs
text=mw.ustring.match(text,"%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
return frame:preprocess(p._lead(args[1] or pargs[1]))
end
return p
1398vdi87pexw8a4xh1766n9cr3i6ee
797170
797169
2018-04-24T10:24:44Z
en>Certes
0
Remove <ref name="Foo" /> for ref cited elsewhere
797170
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename)
if not pagename then return "" end
title=mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
text=title.getContent(title) or ""
text=mw.ustring.gsub(text,"%c%s*==.*","") -- remove first heading and everything after it
text=mw.ustring.gsub(text,"<noinclude>.-</noinclude>","") -- remove noinclude bits
text=mw.ustring.gsub(text,"^%A*%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
text=mw.ustring.gsub(text,"<%s*ref[^>]-/%s*>","") -- remove refs cited elsewhere
text=mw.ustring.gsub(text,"<%s*ref.->.-<%s*/%s*ref%s*>","") -- remove refs
text=mw.ustring.match(text,"%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
return frame:preprocess(p._lead(args[1] or pargs[1]))
end
return p
7fbgxob315aygps0lsew1s7ur6gnu7r
797171
797170
2018-04-24T13:46:43Z
en>Certes
0
Fix: ensure multiple templates are removed from start.
797171
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename)
if not pagename then return "" end
title=mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
text=title.getContent(title) or ""
text=mw.ustring.gsub(text,"%c%s*==.*","") -- remove first heading and everything after it
text=mw.ustring.gsub(text,"<noinclude>.-</noinclude>","") -- remove noinclude bits
repeat
oldtext=text
text=mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text==oldtext
text=mw.ustring.gsub(text,"<%s*ref[^>]-/%s*>","") -- remove refs cited elsewhere
text=mw.ustring.gsub(text,"<%s*ref.->.-<%s*/%s*ref%s*>","") -- remove refs
text=mw.ustring.match(text,"%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
return frame:preprocess(p._lead(args[1] or pargs[1]))
end
return p
jfetv6doezr406o093wvhjnl5kgy7cs
797172
797171
2018-04-25T10:45:34Z
en>Certes
0
Remove footnotes {{efn|not valid in Tennessee}} etc.
797172
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename)
if not pagename then return "" end
title=mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
text=title.getContent(title) or ""
text=mw.ustring.gsub(text,"%c%s*==.*","") -- remove first heading and everything after it
text=mw.ustring.gsub(text,"<noinclude>.-</noinclude>","") -- remove noinclude bits
repeat
oldtext=text
text=mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text==oldtext
text=mw.ustring.gsub(text,"{{%s*[Ee]fn%s*|.-}}","") -- remove {{efn|footnote}}
text=mw.ustring.gsub(text,"{{%s*[Ee]fn-la%s*|.-}}","") -- {{efn-la}} alias for {{efn}}
text=mw.ustring.gsub(text,"{{%s*[Ee]l[mn]%s*|.-}}","") -- {{elm}} and {{eln}} alias for {{efn}}
text=mw.ustring.gsub(text,"<%s*ref[^>]-/%s*>","") -- remove refs cited elsewhere
text=mw.ustring.gsub(text,"<%s*ref.->.-<%s*/%s*ref%s*>","") -- remove refs
text=mw.ustring.match(text,"%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
return frame:preprocess(p._lead(args[1] or pargs[1]))
end
return p
fvb7we48ifxnzbf8f05x7gfh37g7w6j
797173
797172
2018-04-25T11:46:33Z
en>Certes
0
Add paragraphs= argument; improve layout
797173
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, paragraphlist)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title.getContent(title) or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
repeat
local oldtext=text
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text == oldtext
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
newtext = newtext .. sep .. paras[p]
sep = "\n\n"
end
text = newtext
end
text = mw.ustring.gsub(text, "{{%s*[Ee]fn%s*|.-}}", "") -- remove {{efn|footnote}}
text = mw.ustring.gsub(text, "{{%s*[Ee]fn-la%s*|.-}}", "") -- {{efn-la}} alias for {{efn}}
text = mw.ustring.gsub(text, "{{%s*[Ee]l[mn]%s*|.-}}", "") -- {{elm}} and {{eln}} alias for {{efn}}
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.match(text, "%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local parasets = mw.text.split(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local paralist = {}
for _, ps in pairs(parasets) do
local min, max = mw.ustring.match(ps,"^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(ps,"^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(paralist, p) end
end
end
return frame:preprocess(p._lead(args[1] or pargs[1], paralist))
end
return p
ac9vgvfbtxg46jmy5lqn75zuuz02m58
797174
797173
2018-04-25T12:19:55Z
en>Certes
0
Allow page name to be a wikilink
797174
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, paragraphlist)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title.getContent(title) or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
repeat
local oldtext=text
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text == oldtext
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
newtext = newtext .. sep .. paras[p]
sep = "\n\n"
end
text = newtext
end
text = mw.ustring.gsub(text, "{{%s*[Ee]fn%s*|.-}}", "") -- remove {{efn|footnote}}
text = mw.ustring.gsub(text, "{{%s*[Ee]fn-la%s*|.-}}", "") -- {{efn-la}} alias for {{efn}}
text = mw.ustring.gsub(text, "{{%s*[Ee]l[mn]%s*|.-}}", "") -- {{elm}} and {{eln}} alias for {{efn}}
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.match(text, "%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local parasets = mw.text.split(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local paralist = {}
for _, ps in pairs(parasets) do
local min, max = mw.ustring.match(ps,"^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(ps,"^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(paralist, p) end
end
end
return frame:preprocess(p._lead(pagename, paralist))
end
return p
7zdpasicxy54dzsmzlxrvrqyqp1lgsp
797175
797174
2018-04-25T12:50:09Z
en>Certes
0
Calling getContent more properly (no functional difference)
797175
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, paragraphlist)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
repeat
local oldtext=text
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text == oldtext
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
newtext = newtext .. sep .. paras[p]
sep = "\n\n"
end
text = newtext
end
text = mw.ustring.gsub(text, "{{%s*[Ee]fn%s*|.-}}", "") -- remove {{efn|footnote}}
text = mw.ustring.gsub(text, "{{%s*[Ee]fn-la%s*|.-}}", "") -- {{efn-la}} alias for {{efn}}
text = mw.ustring.gsub(text, "{{%s*[Ee]l[mn]%s*|.-}}", "") -- {{elm}} and {{eln}} alias for {{efn}}
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.match(text, "%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local parasets = mw.text.split(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local paralist = {}
for _, ps in pairs(parasets) do
local min, max = mw.ustring.match(ps,"^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(ps,"^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(paralist, p) end
end
end
return frame:preprocess(p._lead(pagename, paralist))
end
return p
t0wn1m5e59slol1s1ljymgooq4o81bz
797176
797175
2018-04-25T12:56:31Z
en>Certes
0
Do not try to append paragraphs whose numbers which exceed the number actually present
797176
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, paragraphlist)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
repeat
local oldtext=text
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text == oldtext
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
if paras[p] then newtext = newtext .. sep .. paras[p] end -- else p exceeds number of paragraphs found
sep = "\n\n"
end
text = newtext
end
text = mw.ustring.gsub(text, "{{%s*[Ee]fn%s*|.-}}", "") -- remove {{efn|footnote}}
text = mw.ustring.gsub(text, "{{%s*[Ee]fn-la%s*|.-}}", "") -- {{efn-la}} alias for {{efn}}
text = mw.ustring.gsub(text, "{{%s*[Ee]l[mn]%s*|.-}}", "") -- {{elm}} and {{eln}} alias for {{efn}}
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.match(text, "%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local parasets = mw.text.split(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local paralist = {}
for _, ps in pairs(parasets) do
local min, max = mw.ustring.match(ps,"^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(ps,"^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(paralist, p) end
end
end
return frame:preprocess(p._lead(pagename, paralist))
end
return p
016jvxj0n7184m770zob1wuhsrjqb9a
797177
797176
2018-04-25T20:21:28Z
en>Certes
0
Get rid of more ref and footnote templates
797177
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, paragraphlist)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
repeat
local oldtext=text
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text == oldtext
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
if paras[p] then newtext = newtext .. sep .. paras[p] end -- else p exceeds number of paragraphs found
sep = "\n\n"
end
text = newtext
end
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]fb"} do -- remove refs and footnotes
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "")
end
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.match(text, "%C*'''.*") or text -- start at the first line with bold text, if any
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local parasets = mw.text.split(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local paralist = {}
for _, ps in pairs(parasets) do
local min, max = mw.ustring.match(ps,"^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(ps,"^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(paralist, p) end
end
end
return frame:preprocess(p._lead(pagename, paralist))
end
return p
ergg2fuofrfxfs7n3g5vlipz8y1umuj
797178
797177
2018-04-25T20:51:08Z
en>Certes
0
Remove files and images. Remove initial white space rather than seeking bold text.
797178
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, paragraphlist)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
repeat
local oldtext=text
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text == oldtext
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
if paras[p] then newtext = newtext .. sep .. paras[p] end -- else p exceeds number of paragraphs found
sep = "\n\n"
end
text = newtext
end
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]fb"} do -- remove refs and footnotes
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "")
end
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%[%[%s*[Ff]ile%s*:%C*%]%]", "") -- remove files
text = mw.ustring.gsub(text, "%[%[%s*[Ii]mage%s*:%C*%]%]", "") -- remove images
text = mw.ustring.gsub(text, "^%s*", "") -- remove initial white space
return text
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local parasets = mw.text.split(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local paralist = {}
for _, ps in pairs(parasets) do
local min, max = mw.ustring.match(ps,"^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(ps,"^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(paralist, p) end
end
end
return frame:preprocess(p._lead(pagename, paralist))
end
return p
thm9pqarlj02qqozfht2dj6f39j0i5j
797179
797178
2018-04-25T22:25:18Z
en>Certes
0
Add support for files= to show selected images from the source
797179
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, options)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
repeat
local oldtext=text
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text == oldtext
local paragraphlist = options.paragraphs or {}
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
if paras[p] then newtext = newtext .. sep .. paras[p] end -- else p exceeds number of paragraphs found
sep = "\n\n"
end
text = newtext
end
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]fb"} do -- remove refs and footnotes
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "")
end
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
local filelist = options.files or {}
local keepfile = {} -- keepfile[n] is true if we want to keep the nth image
for _, v in pairs(filelist) do
keepfile[v] = true
end
text = mw.ustring.gsub(text, "%[%[%s*[Ii]mage%s*:", "[[File:") -- now we can ignore Image:
local n = 1 -- image count
local text2 = ""
for t, f in mw.ustring.gmatch(text.."\n[[File:#DUMMY#]]", "(.-)(%[%[%s*[Ff]ile%s*:%C*%]%])") do -- split around files
text2 = text2 .. t -- always keep the non-file text
if keepfile[n] then text2 = text2 .. f end -- only keep file text if we want this image
n = n + 1
end
text = mw.ustring.gsub(text2, "^%s*", "") -- remove initial white space
text = mw.ustring.gsub(text, "\n%[%[File:#DUMMY#%]%]$", "") -- remove dummy image
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of numbers, e.g. "1,3-5" → {1,3,4,5}
function p.numberlist(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local nlist = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(nlist, p) end
end
end
return nlist
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local paralist = p.numberlist(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local filelist = p.numberlist(args["files"] or pargs["files"] or "", ",") -- parse file numbers
return frame:preprocess(p._lead(pagename, {paragraphs = paralist, files = filelist}))
end
return p
676w185wz7g2y0ikiosoae7qbdey6nd
797180
797179
2018-04-26T11:49:07Z
en>Certes
0
Remove trailing line feeds, so "{{Transclude text excerpt|Foo}} other text" flows onto the same line
797180
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, options)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
repeat
local oldtext=text
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infoboxes, hatnotes, tags, etc. from the front
until text == oldtext
local paragraphlist = options.paragraphs or {}
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
if paras[p] then newtext = newtext .. sep .. paras[p] end -- else p exceeds number of paragraphs found
sep = "\n\n"
end
text = newtext
end
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]fb"} do -- remove refs and footnotes
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "")
end
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
local filelist = options.files or {}
local keepfile = {} -- keepfile[n] is true if we want to keep the nth image
for _, v in pairs(filelist) do
keepfile[v] = true
end
text = mw.ustring.gsub(text, "%[%[%s*[Ii]mage%s*:", "[[File:") -- now we can ignore Image:
local n = 1 -- image count
local text2 = ""
for t, f in mw.ustring.gmatch(text.."\n[[File:#DUMMY#]]", "(.-)(%[%[%s*[Ff]ile%s*:%C*%]%])") do -- split around files
text2 = text2 .. t -- always keep the non-file text
if keepfile[n] then text2 = text2 .. f end -- only keep file text if we want this image
n = n + 1
end
text = mw.ustring.gsub(text2, "^%s*", "") -- remove initial white space
text = mw.ustring.gsub(text, "\n%[%[File:#DUMMY#%]%]$", "") -- remove dummy image
text = mw.ustring.gsub(text, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of numbers, e.g. "1,3-5" → {1,3,4,5}
function p.numberlist(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local nlist = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(nlist, p) end
end
end
return nlist
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local paralist = p.numberlist(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local filelist = p.numberlist(args["files"] or pargs["files"] or "", ",") -- parse file numbers
return frame:preprocess(p._lead(pagename, {paragraphs = paralist, files = filelist}))
end
return p
9m5ibnd7xf67y2vhpasl63bf9xpjpvz
797181
797180
2018-04-26T16:58:30Z
en>Certes
0
Remove HTML comments from between initial templates. Remove refs earlier to cope with incomplete citation templates nested in infoboxes.
797181
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, options)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
repeat -- remove initial HTML comments and templates such as hatnotes
local oldtext=text
text = mw.ustring.gsub(text,"^%s*<!%-%-.-%-%->%s*","") -- remove HTML comment from front
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infobox, hatnote, tag, etc. from front
until text == oldtext
local paragraphlist = options.paragraphs or {}
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
if paras[p] then newtext = newtext .. sep .. paras[p] end -- else p exceeds number of paragraphs found
sep = "\n\n"
end
text = newtext
end
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]fb"} do -- remove refs and footnotes
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "")
end
local filelist = options.files or {}
local keepfile = {} -- keepfile[n] is true if we want to keep the nth image
for _, v in pairs(filelist) do
keepfile[v] = true
end
text = mw.ustring.gsub(text, "%[%[%s*[Ii]mage%s*:", "[[File:") -- now we can ignore Image:
local n = 1 -- image count
local text2 = ""
for t, f in mw.ustring.gmatch(text.."\n[[File:#DUMMY#]]", "(.-)(%[%[%s*[Ff]ile%s*:%C*%]%])") do -- split around files
text2 = text2 .. t -- always keep the non-file text
if keepfile[n] then text2 = text2 .. f end -- only keep file text if we want this image
n = n + 1
end
text = mw.ustring.gsub(text2, "^%s*", "") -- remove initial white space
text = mw.ustring.gsub(text, "\n%[%[File:#DUMMY#%]%]$", "") -- remove dummy image
text = mw.ustring.gsub(text, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of numbers, e.g. "1,3-5" → {1,3,4,5}
function p.numberlist(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local nlist = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(nlist, p) end
end
end
return nlist
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local paralist = p.numberlist(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local filelist = p.numberlist(args["files"] or pargs["files"] or "", ",") -- parse file numbers
return frame:preprocess(p._lead(pagename, {paragraphs = paralist, files = filelist}))
end
return p
7zex7tlm96m1pch90ih89kir58kkpmw
797182
797181
2018-04-26T17:14:05Z
en>Certes
0
Remove trailing templates such as toc
797182
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, options)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
repeat -- remove initial HTML comments and templates such as hatnotes
local oldtext=text
text = mw.ustring.gsub(text,"^%s*<!%-%-.-%-%->%s*","") -- remove HTML comment from front
text = mw.ustring.gsub(text,"^%A-%b{}%s*","") -- remove infobox, hatnote, tag, etc. from front
until text == oldtext
repeat -- remove final HTML comments and templates such as table of contents
local oldtext=text
text = mw.ustring.gsub(text,"%s*<!%-%-.-%-%->%s*$","") -- remove HTML comment from back
text = mw.ustring.gsub(text,"%s*%b{}%s*$","") -- remove toc, etc. from back
until text == oldtext
local paragraphlist = options.paragraphs or {}
if #paragraphlist > 0 then -- limit to requested paragraphs e.g. {1, 3, 4, 5}
local paras = mw.text.split(text, "\n%s*\n") -- %s* may include \n if three or more appear together
local sep="" -- no separator before first paragraph
local newtext = ""
for _, p in pairs(paragraphlist) do
if paras[p] then newtext = newtext .. sep .. paras[p] end -- else p exceeds number of paragraphs found
sep = "\n\n"
end
text = newtext
end
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]fb"} do -- remove refs and footnotes
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "")
end
local filelist = options.files or {}
local keepfile = {} -- keepfile[n] is true if we want to keep the nth image
for _, v in pairs(filelist) do
keepfile[v] = true
end
text = mw.ustring.gsub(text, "%[%[%s*[Ii]mage%s*:", "[[File:") -- now we can ignore Image:
local n = 1 -- image count
local text2 = ""
for t, f in mw.ustring.gmatch(text.."\n[[File:#DUMMY#]]", "(.-)(%[%[%s*[Ff]ile%s*:%C*%]%])") do -- split around files
text2 = text2 .. t -- always keep the non-file text
if keepfile[n] then text2 = text2 .. f end -- only keep file text if we want this image
n = n + 1
end
text = mw.ustring.gsub(text2, "^%s*", "") -- remove initial white space
text = mw.ustring.gsub(text, "\n%[%[File:#DUMMY#%]%]$", "") -- remove dummy image
text = mw.ustring.gsub(text, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of numbers, e.g. "1,3-5" → {1,3,4,5}
function p.numberlist(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local nlist = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do table.insert(nlist, p) end
end
end
return nlist
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local paralist = p.numberlist(args["paragraphs"] or pargs["paragraphs"] or "", ",") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local filelist = p.numberlist(args["files"] or pargs["files"] or "", ",") -- parse file numbers
return frame:preprocess(p._lead(pagename, {paragraphs = paralist, files = filelist}))
end
return p
pf087lbcvbt95uzhjq4kwk4dg11lve7
797183
797182
2018-04-26T23:43:45Z
en>Certes
0
Major overhaul to deal with more permutations of templates, comments and images preceding the desired text
797183
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, options)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:%C*%]%]%s*") -- [[File: ... ]]
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:%C*%]%]%s*") -- or [[Image: ... ]]
if token then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5" }
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
local fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
local text = p._lead(pagename, {paraflags = paraflags, fileflags = fileflags})
return frame:preprocess(text)
end
return p
3tq1dok6i7db5y0449qjxb0d2plh72w
797184
797183
2018-04-27T18:03:32Z
en>Certes
0
Add more= option
797184
Scribunto
text/plain
local p = {}
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, options)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:%C*%]%]%s*") -- [[File: ... ]]
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:%C*%]%]%s*") -- or [[Image: ... ]]
if token then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = p._lead(pagename, options)
return frame:preprocess(text)
end
return p
dz1e674jxbaa3b0192zg8v8uwt3m8pe
797185
797184
2018-04-27T18:46:57Z
en>Certes
0
Handle redirects by transcluding the target
797185
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, options)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:%C*%]%]%s*") -- [[File: ... ]]
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:%C*%]%]%s*") -- or [[Image: ... ]]
if token then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = p._lead(pagename, options)
return frame:preprocess(text)
end
return p
idpoxkrov8t7vaaxt0tqh7ied5tbc8i
797186
797185
2018-04-28T13:22:27Z
en>Certes
0
Bug fix: properly match [[File: and [[Image: which flow over multiple lines or occupy only part of a line
797186
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagename, options)
if not pagename then return "" end -- Return blank text rather than an error splurge
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagename = args[1] or pargs[1] or ""
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = p._lead(pagename, options)
return frame:preprocess(text)
end
return p
pn0sfr37m9njqsdj012ltb8ndxyz4a1
797187
797186
2018-04-28T14:12:11Z
en>Certes
0
Adding random article feature
797187
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
if not pagenames or #pagenames < 1 then return "" end -- Return blank text rather than an error splurge
local pagename = pagenames[1]
if #pagenames > 1 then -- we could do this even with one page, but it would be inefficient
math.randomseed(os.time())
pagename = pagenames[math.random(#pagenames)] --pick a random title
end
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
-- Accept any number of page names. If more than one, we'll pick one randomly
local pagenames = {}
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and not args[i] then table.insert(pagenames, p) end
end
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Function lead is just function random picking from a list of one article,
-- but we advertise different entry points in case they should differ in future
function p.random(frame)
return p.lead(frame)
end
return p
025m8kspwvj2egfuc1rcyb3f7t5v1ho
797188
797187
2018-04-28T14:32:32Z
en>Certes
0
Strip leading and trailing white space from article names
797188
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
if not pagenames or #pagenames < 1 then return "" end -- Return blank text rather than an error splurge
local pagename = pagenames[1]
if #pagenames > 1 then -- we could do this even with one page, but it would be inefficient
math.randomseed(os.time())
pagename = pagenames[math.random(#pagenames)] --pick a random title
end
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
-- Accept any number of page names. If more than one, we'll pick one randomly
local pagenames = {}
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and not args[i] then table.insert(pagenames, p) end
end
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Function lead is just function random picking from a list of one article,
-- but we advertise different entry points in case they should differ in future
function p.random(frame)
return p.lead(frame)
end
return p
19j92ulml1g5bcbrmc2l5xoi6zimhot
797189
797188
2018-04-28T14:45:08Z
en>Certes
0
Removing {{#tag:ref ... }}
797189
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
if not pagenames or #pagenames < 1 then return "" end -- Return blank text rather than an error splurge
local pagename = pagenames[1]
if #pagenames > 1 then -- we could do this even with one page, but it would be inefficient
math.randomseed(os.time())
pagename = pagenames[math.random(#pagenames)] --pick a random title
end
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Entry point for template callers using #invoke:
function p.lead(frame)
-- args = { 1 = page name, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
-- Accept any number of page names. If more than one, we'll pick one randomly
local pagenames = {}
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and not args[i] then table.insert(pagenames, p) end
end
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Function lead is just function random picking from a list of one article,
-- but we advertise different entry points in case they should differ in future
function p.random(frame)
return p.lead(frame)
end
return p
rw4kuv6jj0jklb2gjiyyuh8mbui0f61
797190
797189
2018-05-03T13:55:31Z
en>Certes
0
For lead, ignore all but the first unnamed argument
797190
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
if not pagenames or #pagenames < 1 then return "" end -- Return blank text rather than an error splurge
local pagename = pagenames[1]
if #pagenames > 1 then -- we could do this even with one page, but it would be inefficient
math.randomseed(os.time())
pagename = pagenames[math.random(#pagenames)] --pick a random title
end
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return "" end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent() or ""
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
function p.leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return p.leadrandom(frame, false) end
function p.random(frame) return p.leadrandom(frame, true) end
return p
ofmqba7vqxuwcoxycr1agu90cksbflx
797191
797190
2018-05-03T15:30:00Z
en>Certes
0
Improve error handling
797191
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
--if 1 then return "options.errors=("..options.errors..") errors=("..errors..")" end
if not pagenames or #pagenames < 1 then return p.err("No page names given") end
local pagename = pagenames[1]
if #pagenames > 1 then -- we could do this even with one page, but it would be inefficient
math.randomseed(os.time())
pagename = pagenames[math.random(#pagenames)] --pick a random title
end
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if not pagename then return p.err("Nil page name") end
if pagename == "" then return p.err("Blank page name") end
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return p.err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent()
if not text then return p.err("No page for name " .. pagename) end
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Return blank text, or an error message if requested
function p.err(text, options)
if errors then error(text, 2) end
return ""
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
function p.leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return p.leadrandom(frame, false) end
function p.random(frame) return p.leadrandom(frame, true) end
return p
gmg25fhqwbirdivt9aaemydwe1dplbr
797192
797191
2018-05-03T16:26:58Z
en>Certes
0
If the random page is unavailable, bring on a substitute
797192
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return p.err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return p.err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return p.err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Return blank text, or an error message if requested
function p.err(text, options)
if errors then error(text, 2) end
return ""
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
function p.leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return p.leadrandom(frame, false) end
function p.random(frame) return p.leadrandom(frame, true) end
return p
kri78n05fcd2yidh0j3t3gie3buox1m
797193
797192
2018-05-06T18:48:03Z
en>Certes
0
Also remove [[Template:Rp]]
797193
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return p.err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return p.err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return p.err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Return blank text, or an error message if requested
function p.err(text, options)
if errors then error(text, 2) end
return ""
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
function p.leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return p.leadrandom(frame, false) end
function p.random(frame) return p.leadrandom(frame, true) end
return p
82nif1c8w0lzrkpsujs85ihldkdkbdp
797194
797193
2018-05-09T00:26:54Z
en>Certes
0
Remove imagemap
797194
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return p.err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return p.err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return p.err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then t = t .. token end -- keep comments and templates only within text body
else
token = mw.ustring.match(text, "^%[%[%s*[Ff]ile%s*:") -- [[File: ...
or mw.ustring.match(text, "^%[%[%s*[Ii]mage%s*:") -- or [[Image: ...
if token then
token = mw.ustring.match(text, "^%b[]%s*") -- match [[...]] to handle nesting
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Return blank text, or an error message if requested
function p.err(text, options)
if errors then error(text, 2) end
return ""
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
function p.numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
function p.leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = p.numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = p.numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return p.leadrandom(frame, false) end
function p.random(frame) return p.leadrandom(frame, true) end
return p
e60uqlijk1fxc8dtpkaib2texkr87wh
797195
797194
2018-05-09T18:41:11Z
en>Certes
0
Parse "File:" within templates within preamble (testcase: Man Utd). Strip citation/disambiguation needed templates. Declare functions as local.
797195
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Return blank text, or an error message if requested
local function err(text, options)
if errors then error(text, 2) end
return ""
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a comment, template, image or paragraph
local token = mw.ustring.match(text, "^%s*<!%-%-.-%-%->%s*") -- <!--HTML comment-->
or mw.ustring.match(text, "^%b{}%s*") or false -- or {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
else -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false)
if image then -- keep comments and templates only within text body
image = mw.ustring.gsub(image, "|%s*frameless", "|frame") -- excerpt needs a frame to flow around, even if infobox doesn't
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. image .. "\n" end
end
end
else
token = parseimage(text, true)
if token then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
5an2b1lhp3cto4dkw31t3i6m2qjhvwx
797196
797195
2018-05-09T19:38:54Z
en>Certes
0
Parse image= etc. within infobox. Remove HTML comments earlier in case they lurk within infoboxes.
797196
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[File:" .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|caption=" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "|frame]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
else -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image then -- keep comments and templates only within text body
image = mw.ustring.gsub(image, "|%s*frameless", "|frame") -- excerpt needs a frame to flow around, even if infobox doesn't
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. image end
end
end
else
token = parseimage(text, true)
if token then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
b3xzqzmq8lyepv10hpka2ecmzt9wxc8
797197
797196
2018-05-09T20:09:31Z
en>Certes
0
Thumb sized images
797197
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[File:" .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|caption=" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "|thumb]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
else -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image then -- keep comments and templates only within text body
image = mw.ustring.gsub(image, "|%s*frameless", "|frame") -- excerpt needs a frame to flow around, even if infobox doesn't
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. image end
end
end
else
token = parseimage(text, true)
if token then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
9zukxr7vkrm5akxry8sv82mezm3h7bl
797198
797197
2018-05-09T22:47:36Z
en>Certes
0
Reduce image obtained from infobox to thumb size for [[File:... syntax too
797198
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[File:" .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|caption=" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "|thumb]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
else -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image then -- keep comments and templates only within text body
image = mw.ustring.gsub(image, "|%s*frameless", "|thumb") -- excerpt needs a frame to flow around, even if infobox doesn't
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. image end
end
end
else
token = parseimage(text, true)
if token then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
qnay21cyw3kpljq4td3nk72g1jpy4o3
797199
797198
2018-05-13T11:42:23Z
en>Certes
0
Check for non-free files, including a fix developed in sandbox by [[Evad37]]
797199
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:([^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[File:" .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|caption=" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "|thumb]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
image = mw.ustring.gsub(image, "|%s*frameless", "|thumb") -- excerpt needs a frame to flow around, even if infobox doesn't
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. image end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
nr4yxqhl7u7prdjqiqfdn5v5b1nd0x4
797200
797199
2018-05-13T12:16:06Z
en>Certes
0
Convert infobox image to thumbnail, even if extracted from an enclosed "File:" link. Also fixed a bug with caption= appearing.
797200
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:([^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[File:" .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "%]%]%s*$", "|thumb]]")
end
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. image end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then t = t .. token end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
owzrzuncpvpwd2juzehr3y9mlc5h5hm
797201
797200
2018-05-13T12:31:27Z
en>Certes
0
Adding fileargs= to allow images on the left. Minor efficiency improvement (skip text processing on File:... if this image not wanted)
797201
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:([^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[File:" .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. token
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.fileargs = args["fileargs"] or pargs["fileargs"]
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
rv8eliqq1end256uch9j2drpgnflw8y
797202
797201
2018-05-13T12:53:35Z
en>Certes
0
image bug fixes
797202
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[File:" .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.fileargs = args["fileargs"] or pargs["fileargs"]
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
01onwy7h4f8ciedkrtifvotqei63vxj
797203
797202
2018-05-16T16:29:27Z
en>Certes
0
Accept image=File:Foo or image=Image:Foo as alternatives to image=Foo
797203
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.fileargs = args["fileargs"] or pargs["fileargs"]
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
lz4lvgvaljvzxbnzx1in7wdavm12miy
797204
797203
2018-05-18T11:37:39Z
en>Certes
0
Adding Refn to list of removable templates
797204
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.fileargs = args["fileargs"] or pargs["fileargs"]
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
bjc6wj0naoo4h8ziyezxljo82afsp2y
797205
797204
2018-05-18T20:10:44Z
en>Certes
0
Parse templates more carefully to catch unwanted templates with other templates nested inside. Also removing a few redundant % signs.
797205
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (replace by nothing) else return nil (keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.fileargs = args["fileargs"] or pargs["fileargs"]
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p
7rfejmtv8p2i5gj4ut99vkyxirjingz
797206
797205
2018-05-19T12:05:11Z
en>Certes
0
Overhaul argument handling; add support for selected article template
797206
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (replace by nothing) else return nil (keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlenumber)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end
local pagenames = {}
local articlecount = #args
if articlecount < 1 then err("No articles provided") end
if articlenumber then
if type(articlenumber) == "string" then articlenumber = args[articlenumber] end
-- normalise selected article into the range 1..#args
articlenumber = articlenumber % articlecount
if articlenumber == 0 then articlenumber = articlecount end
pagenames = { args[articlenumber] } -- For lead, ignore all but the first unnamed argument
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end
function p.random(frame) return invoke(frame) end
function p.selected(frame) return invoke(frame, "selected") end
return p
nckbr74qlb3902xjkgdwfl0i5ntqoi5
797207
797206
2018-05-19T13:08:25Z
en>Certes
0
Accept selected= as a string with a numeric value, e.g. "3"
797207
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (replace by nothing) else return nil (keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlenumber)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end
local pagenames = {}
local articlecount = #args
if articlecount < 1 then err("No articles provided") end
if articlenumber then
articlenumber = tonumber(articlenumber) or tonumber(args[articlenumber])
-- normalise selected article into the range 1..#args
articlenumber = articlenumber % articlecount
if articlenumber == 0 then articlenumber = articlecount end
pagenames = { args[articlenumber] } -- For lead, ignore all but the first unnamed argument
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end
function p.random(frame) return invoke(frame) end
function p.selected(frame) return invoke(frame, "selected") end
return p
ha9p9qltqqqa5l44y3xhgogc887wf7q
797208
797207
2018-05-19T14:17:22Z
en>Certes
0
Supporting named article keys in Transclude selected excerpt. Dispose of Efn-ua templates.
797208
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (replace by nothing) else return nil (keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end
function p.random(frame) return invoke(frame) end
function p.selected(frame) return invoke(frame, "selected") end
return p
a0zlfce4j38wohf52o6r4kaqjnb2ncj
797209
797208
2018-05-20T15:25:45Z
en>Certes
0
Handle {{Sfnm}}
797209
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (replace by nothing) else return nil (keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end
function p.random(frame) return invoke(frame) end
function p.selected(frame) return invoke(frame, "selected") end
return p
h8fw1x231357pl2cdm82b6jgmsnppn6
797210
797209
2018-05-20T16:15:53Z
en>Certes
0
Limit File: to images to exclude audio files and other detritus. Parse image_flag= (Infobox country) and PD_image=.
797210
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf
if not mw.ustring.match(page, "%.[Gg][Ii][Ff]%s*$")
and not mw.ustring.match(page, "%.[Jj][Pp][Ee]?[Gg]%s*$")
and not mw.ustring.match(page, "%.[Pp][Nn][Gg]%s*$")
and not mw.ustring.match(page, "%.[Ss][Vv][Gg]%s*$")
and not mw.ustring.match(page, "%.[Tt][Ii][Ff][Ff]%s*$")
and not mw.ustring.match(page, "%.[Xx][Cc][Ff]%s*$") then return false end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*PD_image%s*=%s*([^}|]-)")
or mw.ustring.match(text, "|%s*image_flag%s*=%s*([^}|]-)")
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (replace by nothing) else return nil (keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end
function p.random(frame) return invoke(frame) end
function p.selected(frame) return invoke(frame, "selected") end
return p
5f1ofyalno34xmw11p30cdvcu6ia6en
797211
797210
2018-05-20T20:22:53Z
en>Certes
0
Improve comments; minor bug fix (parsing [[Image:...]] within paragraph)
797211
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitability
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not mw.ustring.match(page, "%.[Gg][Ii][Ff]%s*$")
and not mw.ustring.match(page, "%.[Jj][Pp][Ee]?[Gg]%s*$")
and not mw.ustring.match(page, "%.[Pp][Nn][Gg]%s*$")
and not mw.ustring.match(page, "%.[Ss][Vv][Gg]%s*$")
and not mw.ustring.match(page, "%.[Tt][Ii][Ff][Ff]%s*$")
and not mw.ustring.match(page, "%.[Xx][Cc][Ff]%s*$") then return false end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*PD_image%s*=%s*([^}|]-)") -- or its known alternatives such as...
or mw.ustring.match(text, "|%s*image_flag%s*=%s*([^}|]-)") -- image_flag= from Infobox country
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant optional parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if inlead then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = parseimage(token, false) or argimage(token) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true -- we got a paragraph, so we are inside the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
jdntz2yjaa9ar69qy1me02m4wq8dt7c
797212
797211
2018-05-20T21:37:11Z
en>Certes
0
Make the bold title near the start of the article into a wikilink to the article
797212
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitability
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not mw.ustring.match(page, "%.[Gg][Ii][Ff]%s*$")
and not mw.ustring.match(page, "%.[Jj][Pp][Ee]?[Gg]%s*$")
and not mw.ustring.match(page, "%.[Pp][Nn][Gg]%s*$")
and not mw.ustring.match(page, "%.[Ss][Vv][Gg]%s*$")
and not mw.ustring.match(page, "%.[Tt][Ii][Ff][Ff]%s*$")
and not mw.ustring.match(page, "%.[Xx][Cc][Ff]%s*$") then return false end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*PD_image%s*=%s*([^}|]-)") -- or its known alternatives such as...
or mw.ustring.match(text, "|%s*image_flag%s*=%s*([^}|]-)") -- image_flag= from Infobox country
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant optional parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if inlead then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = parseimage(token, false) or argimage(token) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true -- we got a paragraph, so we are inside the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-'''+)(.-)'''", function(a, b) -- replace '''Foo''' by '''[[pagename|Foo]] if early in article and not wikilinked
if mw.ustring.len(a) < 100 and not mw.ustring.find(b, "%[") then return a .. "[[" .. pagename .. "|" .. b .. "]]'''" else return nil end
end, 1)
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
697lmukgsima0ntxcy565r2h3a2syj0
797213
797212
2018-05-22T11:04:48Z
en>Certes
0
Bug fix: display title correctly where only part of the bold text is in italics, e.g. '''Foo ''Bar''''' or '''''Foo'' Bar'''
797213
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitability
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not mw.ustring.match(page, "%.[Gg][Ii][Ff]%s*$")
and not mw.ustring.match(page, "%.[Jj][Pp][Ee]?[Gg]%s*$")
and not mw.ustring.match(page, "%.[Pp][Nn][Gg]%s*$")
and not mw.ustring.match(page, "%.[Ss][Vv][Gg]%s*$")
and not mw.ustring.match(page, "%.[Tt][Ii][Ff][Ff]%s*$")
and not mw.ustring.match(page, "%.[Xx][Cc][Ff]%s*$") then return false end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*PD_image%s*=%s*([^}|]-)") -- or its known alternatives such as...
or mw.ustring.match(text, "|%s*image_flag%s*=%s*([^}|]-)") -- image_flag= from Infobox country
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant optional parameters: caption, alt text and image size
token = "[[" -- Add File: unless name already begins File: or Image:
if not (mw.ustring.match(image, "^[Ff]ile%s*:")
or mw.ustring.match(image, "^[Ii]mage%s*:")) then
token = token .. "File:"
end
token = token .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if inlead then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = parseimage(token, false) or argimage(token) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true -- we got a paragraph, so we are inside the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b) -- replace '''Foo''' by '''[[pagename|Foo]] if early in article and not wikilinked
if mw.ustring.len(a) < 100 and not mw.ustring.find(b, "%[") then return a .. "[[" .. pagename .. "|" .. b .. "]]'''" else return nil end
end, 1)
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
cp4j9zgrzqnyljw2sxjakknpr420pjy
797214
797213
2018-05-22T12:16:05Z
en>Certes
0
Prefer image= to [[File:... when extracting an image from an infobox with multiple images. Accept non-standard argument names such as Ship image= and Ship caption=. Refactor to use matchany().
797214
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*([^}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if inlead then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true -- we got a paragraph, so we are inside the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b) -- replace '''Foo''' by '''[[pagename|Foo]] if early in article and not wikilinked
if mw.ustring.len(a) < 100 and not mw.ustring.find(b, "%[") then return a .. "[[" .. pagename .. "|" .. b .. "]]'''" else return nil end
end, 1)
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
18pz41o9gql85pveyhaphhvil3myy9m
797215
797214
2018-05-22T14:36:43Z
en>Certes
0
Parse caption found in infobox better, to skip pipes in nested wikilinks
797215
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if inlead then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true -- we got a paragraph, so we are inside the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b) -- replace '''Foo''' by '''[[pagename|Foo]] if early in article and not wikilinked
if mw.ustring.len(a) < 100 and not mw.ustring.find(b, "%[") then return a .. "[[" .. pagename .. "|" .. b .. "]]'''" else return nil end
end, 1)
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
kcpk1vz94deb46ry8oge23fpbjf83bq
797216
797215
2018-05-22T14:57:58Z
en>Certes
0
When checking whether bold text is early enough to wikilink to the article as a synonym for its title, disregard text before the lead such as image links.
797216
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
dzw8w8s8cbp975rzozatrz59s5f2oxd
797217
797216
2018-05-23T12:43:59Z
en>Certes
0
Remove Featured Article star more vigorously
797217
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
pu1layw9sy56mbbrag7ctduvte3h06e
797218
797217
2018-05-24T22:27:10Z
en>Certes
0
Remove sidebars concealed within the lead
797218
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local function striptemplate(t)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
i6n9imjzo2sgz506bptttfnkkkz7wu2
797219
797218
2018-05-25T10:52:11Z
en>Certes
0
Strip |ref parameter from {{UN Population|ref}} and similar
797219
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then -- found a template
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
kq3o5o4hyi3bcm9tmads9lzziuye12w
797220
797219
2018-05-29T21:40:54Z
en>Certes
0
Treat templates at the start of a line of text as part of that text
797220
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
if mw.ustring.find(line, "%S") then token = nil end -- if anything is left, keep the template: it counts as part of the line
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
fpjmua9x00h34idbiwwth3i73cgglf1
797221
797220
2018-05-30T00:09:20Z
en>Certes
0
Discard template if immediately followed by another template which spans lines
797221
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
if mw.ustring.find(line, "%S") and not mw.ustring.find(line, "^%s*{{") then token = nil end -- if anything is left, other than an incomplete further template, keep the template: it counts as part of the line
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
rd0kokahqmqb1m8ulqa1l75gj1dp079
797222
797221
2018-05-30T00:16:23Z
en>Certes
0
Remove templates before the lead even if followed by an image on the same line
797222
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
d5h2wx4m2jyyaq30rapo7x7mvgiv8zq
797223
797222
2018-05-30T15:13:25Z
en>Certes
0
Remove protection templates
797223
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
errors = options.errors -- set the module level boolean used in local function err
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, articlekey)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local pagenames = {}
local articlecount = #args
if articlekey then -- 1 for lead template; "selected" for selected template
articlekey = tonumber(articlekey) or args[articlekey]
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
else
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.random(frame) return invoke(frame) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
adap53n6kb0gb555bjls2fw19z8ssiq
797224
797223
2018-05-31T11:01:27Z
en>Certes
0
Adding linked function; refactor invoke()
797224
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" then
-- Read named page and find its wikilinks
local page = args[1]
local title = mw.title.new(page) -- Read the named page
if not title then return err("No title for page name " .. page) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
if not text then return err("No content for page name " .. page) end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
for p in mw.ustring.gmatch(text, "%[%[([^%]|#\n]*)") do table.insert(pagenames, p) end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead article}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked article}} reads a randomly selected article linked from the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
3ilkcz37cds35krla7nvffaj43h6k6e
797225
797224
2018-06-01T17:23:28Z
en>Certes
0
Remove "clarification needed", "Primary source inline" and their dozens of aliases
797225
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" then
-- Read named page and find its wikilinks
local page = args[1]
local title = mw.title.new(page) -- Read the named page
if not title then return err("No title for page name " .. page) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
if not text then return err("No content for page name " .. page) end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
for p in mw.ustring.gmatch(text, "%[%[([^%]|#\n]*)") do table.insert(pagenames, p) end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead article}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked article}} reads a randomly selected article linked from the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
t8q34r6qcog6u9wb06y75ba42qip9qi
797226
797225
2018-06-06T14:32:28Z
en>Waggers
0
added "static_image_name" as a possible Infobox image parameter
797226
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" then
-- Read named page and find its wikilinks
local page = args[1]
local title = mw.title.new(page) -- Read the named page
if not title then return err("No title for page name " .. page) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
if not text then return err("No content for page name " .. page) end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
for p in mw.ustring.gmatch(text, "%[%[([^%]|#\n]*)") do table.insert(pagenames, p) end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead article}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked article}} reads a randomly selected article linked from the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
hyu2g7xovefs13lk4fapojmmbipp3w0
797227
797226
2018-06-06T14:38:34Z
en>Waggers
0
added "static_image" as a possible Infobox image parameter
797227
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" then
-- Read named page and find its wikilinks
local page = args[1]
local title = mw.title.new(page) -- Read the named page
if not title then return err("No title for page name " .. page) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
if not text then return err("No content for page name " .. page) end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
for p in mw.ustring.gmatch(text, "%[%[([^%]|#\n]*)") do table.insert(pagenames, p) end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead article}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked article}} reads a randomly selected article linked from the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
tq3xv8ehflquzafs1rkdwn1ahbt5rdd
797228
797227
2018-06-06T14:42:11Z
en>Waggers
0
added other possible image parameters from {{Infobox UK place}}
797228
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" then
-- Read named page and find its wikilinks
local page = args[1]
local title = mw.title.new(page) -- Read the named page
if not title then return err("No title for page name " .. page) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
if not text then return err("No content for page name " .. page) end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
for p in mw.ustring.gmatch(text, "%[%[([^%]|#\n]*)") do table.insert(pagenames, p) end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead article}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked article}} reads a randomly selected article linked from the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
return p
a4zm7lqylniu7r8712gyr811zjn5nfx
797229
797228
2018-06-09T16:45:11Z
en>Certes
0
Adding listitem function; minor fixes
797229
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local title = mw.title.new(page) -- Read the named page
if not title then return err("No title for page name " .. page) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent()
if not text then return err("No content for page name " .. page) end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
return p
or1dsm57fx50tbs4h5cf55arbo094dq
797230
797229
2018-06-10T23:45:50Z
en>Certes
0
Remove stub templates and categories because, in a stub, the whole article resembles a lead.
797230
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
pagename = redir or pagename
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
-- a basic parser to trim down the lead
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
t = t .. token
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local title = mw.title.new(page) -- Read the named page
if not title then return err("No title for page name " .. page) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local text = title:getContent()
if not text then return err("No content for page name " .. page) end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
return p
k8fqtqhal3rk2b94o50yp8q90f6i6sw
797231
797230
2018-06-12T08:28:06Z
en>Evad37
0
generalise some functions, and provide entry points for other Lua modules
797231
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
return p
ban2yzogesyve32cpttoor5x4msxkdx
797232
797231
2018-06-14T10:59:38Z
en>Certes
0
Remove disambiguation templates
797232
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w%s]-%f[%w][Dd]isam[%w%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
return p
tn0zwihf3ym35i8012kfzebl69epfdj
797233
797232
2018-06-18T05:15:03Z
en>Evad37
0
additional entry points for modules
797233
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "{{%s*[Ii]nfobox") then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w%s]-%f[%w][Dd]isam[%w%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
galx4ey8v0fydd6f9xv7zr0w3vqq0ik
797234
797233
2018-06-18T06:15:12Z
en>Evad37
0
catch infoboxes with other names (e.g. {{subspeciesbox}})
797234
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local isInfobox = mw.ustring.match(text, "{{%s*[Ii]nfobox")
if not isInfobox then
-- check by expanding template, to catch infoboxes with other names (e.g. {{subspeciesbox}})
local frame = mw.getCurrentFrame()
local expandedContent = frame:preprocess(text)
isInfobox = mw.ustring.match(expandedContent, "<table[^>]-infobox") or mw.ustring.match(expandedContent, "{|[^\n]*infobox")
end
if isInfobox then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w%s]-%f[%w][Dd]isam[%w%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
bpt0jhlqmggzgy9r1uern7yelmcw6fu
797235
797234
2018-06-18T15:25:45Z
en>Certes
0
Remove DISPLAYTITLE and Short description in case they don't precede the lead
797235
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local isInfobox = mw.ustring.match(text, "{{%s*[Ii]nfobox")
if not isInfobox then
-- check by expanding template, to catch infoboxes with other names (e.g. {{subspeciesbox}})
local frame = mw.getCurrentFrame()
local expandedContent = frame:preprocess(text)
isInfobox = mw.ustring.match(expandedContent, "<table[^>]-infobox") or mw.ustring.match(expandedContent, "{|[^\n]*infobox")
end
if isInfobox then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w%s]-%f[%w][Dd]isam[%w%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
64us2zk74bs09crbrowufpgdnvllmf9
797236
797235
2018-06-19T04:38:51Z
en>Evad37
0
convert imagemaps into standard images
797236
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local isInfobox = mw.ustring.match(text, "{{%s*[Ii]nfobox")
if not isInfobox then
-- check by expanding template, to catch infoboxes with other names (e.g. {{subspeciesbox}})
local frame = mw.getCurrentFrame()
local expandedContent = frame:preprocess(text)
isInfobox = mw.ustring.match(expandedContent, "<table[^>]-infobox") or mw.ustring.match(expandedContent, "{|[^\n]*infobox")
end
if isInfobox then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w%s]-%f[%w][Dd]isam[%w%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
87tecb3py2i7ofuhhirj5k9018320nf
797237
797236
2018-06-20T09:05:02Z
en>Evad37
0
use frame:expandTemplate instead of frame:preprocess for infobox detection
797237
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local templateName = mw.ustring.match(text, "{{%s*(.-)%s*[|}]")
local isInfobox = mw.ustring.match(templateName, "[Ii]nfobox")
if not isInfobox then
-- check by expanding template, to catch infoboxes with other names (e.g. {{subspeciesbox}})
local frame = mw.getCurrentFrame()
local expandedContent = frame:expandTemplate{title = templateName}
isInfobox = mw.ustring.match(expandedContent, "<table[^>]-infobox") or mw.ustring.match(expandedContent, "{|[^\n]*infobox")
end
if isInfobox then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w%s]-%f[%w][Dd]isam[%w%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
beh0ctkrcjm55s5s2f4aksibhlivqei
797238
797237
2018-06-20T10:19:39Z
en>Certes
0
Infobox check: exclude {{MAGICWORD:arg}}: not a template
797238
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local templateName = mw.ustring.match(text, "{{%s*(.-)%s*[|}]")
local isInfobox = mw.ustring.match(templateName, "[Ii]nfobox")
if not isInfobox and not mw.ustring.match(templateName, "^%u+:") then -- exclude {{MAGICWORD:arg}}: not a template
-- check by expanding template, to catch infoboxes with other names (e.g. {{subspeciesbox}})
local frame = mw.getCurrentFrame()
local expandedContent = frame:expandTemplate{title = templateName}
isInfobox = mw.ustring.match(expandedContent, "<table[^>]-infobox") or mw.ustring.match(expandedContent, "{|[^\n]*infobox")
end
if isInfobox then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w%s]-%f[%w][Dd]isam[%w%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*sidebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-stub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
jgd4id5dq4rh8wrg8m6cyd0n1ua1o3c
797239
797238
2018-06-20T10:44:59Z
en>Certes
0
Match Citation templates and tag with capitals (<Ref> etc.)
797239
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local templateName = mw.ustring.match(text, "{{%s*(.-)%s*[|}]")
local isInfobox = mw.ustring.match(templateName, "[Ii]nfobox")
if not isInfobox and not mw.ustring.match(templateName, "^%u+:") then -- exclude {{MAGICWORD:arg}}: not a template
-- check by expanding template, to catch infoboxes with other names (e.g. {{subspeciesbox}})
local frame = mw.getCurrentFrame()
local expandedContent = frame:expandTemplate{title = templateName}
isInfobox = mw.ustring.match(expandedContent, "<table[^>]-infobox") or mw.ustring.match(expandedContent, "{|[^\n]*infobox")
end
if isInfobox then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w%s]-%f[%w][Dd]isam[%w%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
be1i13wls9b5npzakf10xpz4v40kl4j
797240
797239
2018-06-22T01:32:30Z
en>Certes
0
Strip {{Coord}} and aliases
797240
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local templateName = mw.ustring.match(text, "{{%s*(.-)%s*[|}]")
local isInfobox = mw.ustring.match(templateName, "[Ii]nfobox")
if not isInfobox and not mw.ustring.match(templateName, "^%u+:") then -- exclude {{MAGICWORD:arg}}: not a template
-- check by expanding template, to catch infoboxes with other names (e.g. {{subspeciesbox}})
local frame = mw.getCurrentFrame()
local expandedContent = frame:expandTemplate{title = templateName}
isInfobox = mw.ustring.match(expandedContent, "<table[^>]-infobox") or mw.ustring.match(expandedContent, "{|[^\n]*infobox")
end
if isInfobox then
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return token
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
adaywg15rmckb482yi7fkf5tlv9jqqw
797241
797240
2018-06-22T02:03:03Z
en>Evad37
0
don't bother checking if the template is an infobox – preprocessing or expanding templates is expensive, and can cause unwanted side effectes. Also reduce the nesting of if blocks.
797241
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if not image then return nil end
-- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
return token
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
j9kj523kl1aa3xbpsthqtlwds9lbxv4
797242
797241
2018-06-25T16:19:52Z
en>Certes
0
Within infobox, terminate image parameters at newline
797242
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
local image = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover", "static_image_name", "static_image", "image_skyline", "image_shield", "static_image2_name", "static_image2"}, "%s*=%s*(.*)")
if not image then return nil end
-- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*([^\n]*)"))
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^}|\n]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^}|\n]*)")
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
return token
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
lasxw5cwu1b919ggajxuc49o4yvim3u
797243
797242
2018-07-13T08:47:25Z
en>Evad37
0
better image matching
797243
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
local position = mw.ustring.find(text, caption, 0, true)
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
local position = mw.ustring.find(text, altText, 0, true)
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
local position = mw.ustring.find(text, imageSizeMatch, 0, true)
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
81bezv0e3o10foubrqfryjquq8mhc38
797244
797243
2018-07-15T10:43:49Z
en>Certes
0
Adding sectiononly= to exclude subsections
797244
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
local position = mw.ustring.find(text, caption, 0, true)
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
local position = mw.ustring.find(text, altText, 0, true)
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
local position = mw.ustring.find(text, imageSizeMatch, 0, true)
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
dpq686ozxqkvbt1uvwgvisyi86arkg6
797245
797244
2018-07-16T03:15:17Z
en>Evad37
0
TOC templates might not start on a new line
797245
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
local position = mw.ustring.find(text, caption, 0, true)
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
local position = mw.ustring.find(text, altText, 0, true)
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
local position = mw.ustring.find(text, imageSizeMatch, 0, true)
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
990hrnpdcqmubpc1dkio332v83vww90
797246
797245
2018-07-16T03:48:18Z
en>Evad37
0
pass through leadstart, so that bold text linking still works when files are included
797246
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
local position = mw.ustring.find(text, caption, 0, true)
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
local position = mw.ustring.find(text, altText, 0, true)
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
local position = mw.ustring.find(text, imageSizeMatch, 0, true)
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
tejuh4nvvajxozudgr0nsjt63uueqef
797247
797246
2018-07-16T16:00:07Z
en>Certes
0
Minor fix to caption parsing
797247
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
local position = mw.ustring.find(text, altText, 0, true)
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
local position = mw.ustring.find(text, imageSizeMatch, 0, true)
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
gim8mhj1svv5ur1jjlfbvfa8em9zfyc
797248
797247
2018-07-16T17:11:20Z
en>Certes
0
Put images first, to avoid them appearing wholly beneath the end of the text
797248
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
local position = mw.ustring.find(text, altText, 0, true)
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
local position = mw.ustring.find(text, imageSizeMatch, 0, true)
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, noramliesdPagename = getContent(pagename)
if not noramliesdPagename then
return err("No title for page name " .. pagename)
else
pagename = noramliesdPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
1h078d8rcr2xrkg7jydmrr84n8yhinu
797249
797248
2018-07-16T23:26:56Z
en>Certes
0
typos
797249
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if t
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
local position = mw.ustring.find(text, altText, 0, true)
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
local position = mw.ustring.find(text, imageSizeMatch, 0, true)
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|#\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
lzm3qko7gd0atftj2js7rj0s3p74syf
797250
797249
2018-07-16T23:58:41Z
en>Certes
0
Accept Page#Section syntax
797250
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*(.*)") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
images[position] = image
end
for image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
local position = mw.ustring.find(text, image, 0, true)
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
local position = mw.ustring.find(text, altText, 0, true)
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
local position = mw.ustring.find(text, imageSizeMatch, 0, true)
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
8f4vhges9w4cqzzvdcfnx5k340sjzmb
797251
797250
2018-07-19T06:36:12Z
en>Evad37
0
update from sandbox: avoid "pattern over 10,000 characters" errors
797251
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]*[Ii][Mm][Aa][Gg][Ee][^=|]*%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]*[Pp][Hh][Oo][Tt][Oo][^=|]*%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^|{}]*%s*=%s*()([^|{}]*%.%a%a%a%a?)%s*[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for index, image in pairs(images) do
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
dbenvziaj0e1iz88je6wa84xx80zacq
797252
797251
2018-07-19T07:35:46Z
en>Evad37
0
match more images; ensure images are sorted in the order that they appear in the wikitext
797252
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
qskj4zjlof84fr274fz2skd6f8po01i
797253
797252
2018-07-21T05:16:15Z
en>Evad37
0
Get a redirect target without using the expensive title object property .isRedirect; remove TOC behavior switches from excerpts
797253
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__%s*", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
mg2dtjw1z1rvid6vz7aja1e47jb7ajb
797254
797253
2018-07-25T04:09:03Z
en>Evad37
0
nostubs option to avoid picking stubs; don't remove trailing whitespace/newlines when removing TOC behavior switches
797254
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
qtdf780j24y1w7imw6fxhsqbckl4wzy
797255
797254
2018-08-26T21:14:10Z
en>Certes
0
Remove "unreliable source?" tags
797255
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
s7cqsqmu110zbem643qp92odlaajkwa
797256
797255
2018-08-28T07:02:56Z
en>Evad37
0
prevent empty or whitespace-only fileargs being used
797256
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
pyup5ytom9muxfmfeeztbiwvuxw4fmy
797257
797256
2018-09-02T04:05:39Z
en>Evad37
0
catch {{efn}} variants for roman and greek
797257
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[ _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
ko7f676ze3gzprjg6s3zeof14sgmlao
797258
797257
2018-09-02T21:00:09Z
en>Certes
0
Remove {{cite-web}} etc. If a wanted template has unwanted nested templates, purge them too. Example: {{efn-lr... within {{nihongo... in [[Matcha]].
797258
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]*%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(t, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
dqp7dtj6o9zbaznp2qxz22jimx8fdht
797259
797258
2018-09-03T08:29:56Z
en>Evad37
0
fix altText pattern, to avoid bogus file options lint errors
797259
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(t, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
o9x4avpy3zs1yaccn7zy1rvfavqqn5r
797260
797259
2018-09-13T10:35:09Z
en>Evad37
0
replace annotated links with real links before looking for links or list items
797260
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
-- aliases for Primary source inline
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]econdary[ _]+source[ _]+needed",
"[Pp]rimary[ _]+source[ _]+claim", "[Pp]rimary[%- _]+source[%- _]+inline", "[Pp]rimary[%- _]+inline",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(t, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
mv7z59fsyvccbd19xdm51f9fm6mgou1
797261
797260
2018-09-13T10:49:27Z
en>Certes
0
Remove more primary source etc. tags
797261
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf-published[%w_%s]-", "[Uu]ser-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(t, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
bxjt7r5ofemyoye16op1zp9ebtnkytj
797262
797261
2018-09-13T13:03:13Z
en>Certes
0
Escape literal hyphens
797262
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(t, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
48dr9qmde7ecbm9gqwmtzyb6u14ll4k
797263
797262
2018-09-15T20:45:25Z
en>Certes
0
Remove {{Failed verification}} and its multitudinous aliases
797263
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(t, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
7tgjxhqbswr17lbdkp5yqzkekv4gq22
797264
797263
2018-09-22T23:22:07Z
en>Certes
0
Remove Template:When
797264
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(t, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text
if not filesOnly then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
1ayit5u16jf46kngpqqh26ck624z2bn
797265
797264
2018-09-25T10:14:44Z
en>Certes
0
Remove a line which is just a template: typically used for navboxes in footer etc.
797265
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(t, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
bkw1cjd883la6avgtij8htgdlwcauyn
797266
797265
2018-10-04T21:00:00Z
en>Certes
0
Fix bug: when a line contains both nested templates and template with a |ref parameter, e.g. [[Somalia]], remove both
797266
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
t2k1k5inin0cm8r0luniembees5i3x6
797267
797266
2018-10-09T11:59:43Z
en>Certes
0
Extend caption to include a nested template flowed over multiple lines. (Function parsecaption() strips the template out later.)
797267
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
2ds7s82gzl0lzo1cqvge768vz4vn0pc
797268
797267
2018-10-09T12:13:53Z
en>Certes
0
remove DIY hatnote indented with a colon
797268
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
9mw9z15mh4qud0sdiq6xobz6t1qieky
797269
797268
2018-11-05T11:24:48Z
en>Certes
0
Extract a section when processing a redirect to Page#Section. (Pending change in sandbox not promoted yet.)
797269
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??"}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
lczekfhnal5crv8m98oo66n7r2o80sx
797270
797269
2018-11-18T22:43:58Z
en>Certes
0
Remove {{Update}} and chums
797270
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)") do
hasImages = true
images[position] = image
end
for position, image in mw.ustring.gmatch(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]") do
hasImages = true
if not images[position] then
images[position] = image
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
local capture_from = 0
while capture_from < #text do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = #text
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
l9byrihl69nuh97cqq0s7prrj6x5oys
797271
797270
2018-12-06T18:12:42Z
en>Certes
0
Copy Morpeth imagemap fix (mostly by [[User:Evad37]]) from sandbox
797271
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
ly8931mxto2ur57gzg7d217usav64y9
797272
797271
2018-12-22T19:38:12Z
en>Ymblanter
0
Protected "[[Module:Excerpt]]": [[WP:High-risk templates|High-risk Lua module]]: request at [[WP:RFPP]] ([Edit=Require autoconfirmed or confirmed access] (indefinite))
797271
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr]ef[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr]ef.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
ly8931mxto2ur57gzg7d217usav64y9
797273
797272
2018-12-26T00:01:36Z
en>Dreamy Jazz
0
merge from sandbox; handle strange capitalisation of ref tags, which are valid but were not removed (and in some cases causing spew errors)
797273
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii]magemap.->.-<%s*/%s*imagemap%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
dm6hbt71k0b8rmsyjcdrptyufa0hm15
797274
797273
2018-12-26T00:07:39Z
en>Dreamy Jazz
0
merge from sandbox; do the same regex fixes for noinclude and imagemap (just in case)
797274
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
glmn4zfwhelcn8y5qaf2h9ajpvgifdc
797275
797274
2018-12-26T00:40:32Z
en>Dreamy Jazz
0
merge from sandbox; remove [[Template:By whom]] and aliases
797275
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for By Whom
"[Bb]y[ _][Ww]ho%??","[Bb]y[ _][Ww]hom%??"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
lf8haslabg4flscrmue8rrq2dl6e7s8
797276
797275
2018-12-26T00:42:30Z
en>Dreamy Jazz
0
handle [[Template:Citation-needed]]
797276
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for By Whom
"[Bb]y[ _][Ww]ho%??","[Bb]y[ _][Ww]hom%??"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
g2cq8q20exbj8qozmtu22dx7xnw7snv
797277
797276
2018-12-26T12:28:05Z
en>Certes
0
Remove ancestry charts
797277
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for By Whom
"[Bb]y[ _][Ww]ho%??","[Bb]y[ _][Ww]hom%??",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
ae0iskfh73tjr3oi5ngtjoxhnag9fwm
797278
797277
2018-12-26T20:24:26Z
en>Dreamy Jazz
0
add aliases
797278
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
n2zq2u5aam9vj53mfptvey9povk7lay
797279
797278
2018-12-27T03:16:58Z
en>Evad37
0
make sure hatnotes aren't mistaken for the start of the lead
797279
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
735hjyp373oyo59o38fgag6nkcxr4fg
797280
797279
2019-01-07T01:58:32Z
en>Certes
0
<HTML tag>{{template}} now counts as a template on a line of its own, fixes [[Indo-Pakistani wars and conflicts]]
797280
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
t01uomi7axv9fj7lcb6cjemf3789y7s
797281
797280
2019-01-08T12:44:24Z
en>Certes
0
Restore line removed accidentally in previous edit
797281
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
e5xom6x7wmp5h6fngt8h23y87p1d266
797282
797281
2019-01-09T19:01:07Z
en>Certes
0
Remove unmatched {{ etc. which might break an enclosing template such as {{#tag:gallery}}
797282
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "{\27%1\27}"); -- {{sometemplate}} → {E{sometemplate}E} where E represents escape
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "[\27%1\27]");
until text == t
text = text.gsub(text, "([{}%[%]])%1.*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: {E{ → {{, ]E] → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
g3wtvdw29h20lcm6omdxjl0ysej8ny0
797283
797282
2019-01-09T19:25:46Z
en>Certes
0
Bug fix: avoid truncating after ]]]] because second and third brackets don't form a pair
797283
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "{\27%1\27}"); -- {{sometemplate}} → {E{sometemplate}E} where E represents escape
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "[\27%1\27]");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]]E] etc.
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: {E{ → {{, ]E] → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
agl2mfzkjzk89o6jo6u03h25rs161xc
797284
797283
2019-01-10T12:09:08Z
en>Certes
0
More escape characters to deal with text like [comment [[wikilink]]]. Exclude }} within <math> to disregard cases like <math>\sqrt{\hat{x}}</math> (test case: [[Milankovitch cycles]]).
797284
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math *>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]]E] etc.
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
q4udkxcjiyb0fhox9fttbe80yaxpksu
797285
797284
2019-01-10T12:18:32Z
en>Certes
0
Handle <math foo="bar"> like <math>; test case [[Commensurability (mathematics)]]
797285
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]]E] etc.
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
3sih6cyvvizhbf3ek4z8xjd3vestz5e
797286
797285
2019-01-10T21:03:00Z
en>Certes
0
Exclude following | or }} from alt text, after accepting embedded {{foo|bar}} etc. Test case: [[Quickplay Media]] on [[Portal:AT&T]]
797286
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.find(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.find(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.find(altText, "()}}", lookfrom) or len+1,
mw.ustring.find(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]]E] etc.
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
hceo6tm56vb69znfpjoxk0ubi219yn0
797287
797286
2019-01-11T20:17:25Z
en>Dreamy Jazz
0
merge from sandbox; add Cref and others
797287
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.find(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.find(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.find(altText, "()}}", lookfrom) or len+1,
mw.ustring.find(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]]E] etc.
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
3zfhpgxduiqj6yb96izhsr9eige0mk6
797288
797287
2019-01-12T09:16:30Z
en>Dreamy Jazz
0
add IPA needed
797288
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.find(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.find(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.find(altText, "()}}", lookfrom) or len+1,
mw.ustring.find(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]]E] etc.
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
hxernaj8elwqkd4dxzsyn50agy5vgpu
797289
797288
2019-01-12T11:21:46Z
en>Certes
0
Use match instead of find, to capture the current string position with ()
797289
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]]E] etc.
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
cuf52x44feibc6c776559mvw9px910q
797290
797289
2019-01-12T11:28:37Z
en>Certes
0
Remove stray }} etc. at end of text
797290
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
6xqzpn82c6uju2d1fiwm29mcmbajj7u
797291
797290
2019-01-12T11:32:58Z
en>Certes
0
More unwanted templates
797291
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
cvlqlrng7ldx3irsgcczlpyamiqhta3
797292
797291
2019-01-15T11:45:00Z
en>Certes
0
remove musical scores
797292
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
df59z5pnx0yhok2mdotogau9vhagsm0
797293
797292
2019-01-21T10:34:09Z
en>Dreamy Jazz
0
add [[Template:Wikisource-multi]]
797293
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. section .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
f239adqovbzhmy3ototdpr73a1b6xvb
797294
797293
2019-01-25T10:46:01Z
en>Certes
0
Handle section names containing URL-escaped characters and characters special to Lua patterns
797294
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
fy7ojhr1cznxg68dnih454xpvrclw38
797295
797294
2019-02-04T12:23:18Z
en>Certes
0
Replace {{audio}} by its text parameter
797295
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
249hecg3jad3phz9fua2kvj8v0qscjt
797296
797295
2019-02-05T12:01:20Z
en>Dreamy Jazz
0
remove math, chem and chem math
797296
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*(?:[Mm][Aa][Tt][Hh].->.-<%s*/%s*[Mm][Aa][Tt][Hh]%s*>", "") -- remove math
text = mw.ustring.gsub(text, "<%s*(?:[Cc][Hh][Ee][Mm].->.-<%s*/%s*[Cc][Hh][Ee][Mm]%s*>", "") -- remove chem
text = mw.ustring.gsub(text, "<%s*(?:[Cc][Hh][Ee][Mm][ ]*[Mm][Aa][Tt][Hh].->.-<%s*/%s*[Cc][Hh][Ee][Mm][ ]*[Mm][Aa][Tt][Hh]%s*>", "") -- remove chem math
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
heco54v0mdgls4phd8xl0ehntq69wow
797297
797296
2019-02-05T12:01:40Z
en>Dreamy Jazz
0
Undid revision 881879687 by [[Special:Contributions/Dreamy Jazz|Dreamy Jazz]] ([[User talk:Dreamy Jazz|talk]])
797297
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
249hecg3jad3phz9fua2kvj8v0qscjt
797298
797297
2019-02-05T12:09:07Z
en>Dreamy Jazz
0
add explain and others
797298
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
pjkgvgxww9zhuashe3m3xaorkwinboh
797299
797298
2019-02-05T12:18:04Z
en>Dreamy Jazz
0
remove TOC templates
797299
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
ceynhjtpgbv9fmxjwfjqkzrm5xmuerw
797300
797299
2019-02-08T12:30:45Z
en>Dreamy Jazz
0
add [[Template:Inflation/fn]]
797300
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
bxhbm7pdoko8j6rarddvttnielx5f8u
797301
797300
2019-02-10T14:44:59Z
en>Certes
0
Remove sections even if lead is blank
797301
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
r1c4x8en92mslf0r108yssdgpgw6if3
797302
797301
2019-02-11T16:03:02Z
en>Certes
0
Treat cases of Nihongo foot
797302
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
hqis0qtljxluykpilgbpss3e56oa0hj
797303
797302
2019-02-24T13:52:25Z
en>Dreamy Jazz
0
add [[Template:Biblesource]]
797303
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
if title.namespace == 6 then
frame = frame or mw.getCurrentFrame()
return frame:preprocess("{{" .. title.prefixedText .. "}}"), redir or title.prefixedText
else
return title:getContent(), redir or title.prefixedText
end
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc = getContent(page)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Help gsub to remove unwanted templates
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]"
}
if matchany(t, "^{{%s*", unwanted, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
qmfuor3xxi8trgp3197gaiy0gqv1z5p
797304
797303
2019-02-24T21:00:21Z
en>Certes
0
Prevent side-effects of frame:preprocess on image descriptions, such as setting DEFAULTSORT
797304
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
else -- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(filetext .. t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return text, leadstart
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
text, leadstart = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
emveftegn841f82etlkttvc22z4qu69
797305
797304
2019-02-25T10:56:40Z
en>Certes
0
Ignore File: text when linking bold title
797305
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
else -- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local filetext
filetext, text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
c0s9fys6d7e3fzc5b5azpx4rjefty2b
797306
797305
2019-02-25T12:43:07Z
en>Certes
0
Bug fix: check rtitle is truthy to fix [[Portal:Nike, Inc.]]
797306
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local filetext
filetext, text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
psukx2nqv37g9lo0x610jtwhs9qdo05
797307
797306
2019-02-25T13:12:39Z
en>Certes
0
Ensure div tags match
797307
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local filetext
filetext, text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
47oxrgxqot0hg3v7h91kgtc3u216x7i
797308
797307
2019-03-22T19:01:58Z
en>Certes
0
Remove another unwanted hat template
797308
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local filetext
filetext, text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
i8xn74fbpcl11sriapdu6aak9xsfjmr
797309
797308
2019-05-24T00:09:03Z
en>Certes
0
If excerpt has onlyinclude sections, remove those tags and the text outside them
797309
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position);
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. fileargs .. "%1") end
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local filetext
filetext, text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
aumdljs7truwgykbv7zu12ozlmssruz
797310
797309
2019-09-06T12:52:14Z
en>Certes
0
Improve fileargs handling: when adding |left to image specification, remove |right, etc.
797310
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa}; -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", ""); -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1"); -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs);
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[%]|]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local filetext
filetext, text = parse(text, options)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
qgdq8o9w3j1qsciyif4ybcagjaj7v7v
797311
797310
2019-09-12T11:26:46Z
en>Certes
0
Add per-page options
797311
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa}; -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", ""); -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1"); -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs);
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
h7i4pcd6m6lw8pitvobstzz2kiw7mfh
797312
797311
2019-09-12T13:27:02Z
en>Certes
0
Add named image files
797312
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa}; -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", ""); -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1"); -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs);
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = main(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
osilkptk98001wikt31cbgdtb3f7n93
797313
797312
2019-10-20T15:26:08Z
en>Certes
0
Add showall= to show all excerpts from selectable articles
797313
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa}; -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", ""); -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1"); -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs);
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = cleanupText(text, true)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly) return cleanupText(text, leadOnly) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
f6jxfh5ddyytr3llz8w4d11q4076yfh
797314
797313
2020-01-07T18:02:06Z
en>Sophivorus
0
Add option to keep references, see talk page
797314
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa}; -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", ""); -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1"); -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs);
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs = options.keepRefs
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
iyukx0t2wp799whq43itr6s1j0583bw
797315
797314
2020-01-07T18:06:34Z
en>Sophivorus
0
797315
Scribunto
text/plain
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Initialise this big table once to save time
local unwantedTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", {"[Ii]mage:", "[Ff]ile:"}, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa}; -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", ""); -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1"); -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs);
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs = options.keepRefs
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
oimz58jmmrszy8bm7n3c5n5fbxe44t4
797316
797315
2020-01-31T12:14:31Z
en>Sophivorus
0
Update to latest sandbox version
797316
Scribunto
text/plain
-- Local aliases of the file namespace
local fileNamespaces = {
"[Ff]ile",
"[Ii]mage"
}
-- The module keeps all inline templates except these ones
local unwantedInlineTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- The module removes all block templates except these ones
local wantedBlockTemplates = {
"[Hh]istorical populations"
}
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa}; -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", ""); -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1"); -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif options.keepTables and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs);
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$"); -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs
if options.keepRefs == '1' then keepRefs = true else keepRefs = false end
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27"); -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2"); -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27");
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", ""); -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", ""); -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", ""); -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
a8ck57zn3wnxyneelok81wo04abu731
797317
797316
2020-02-01T17:13:03Z
en>Certes
0
list= option to add a collapsed list of pages which might appear; remove unnecessary semicolons
797317
Scribunto
text/plain
-- Local aliases of the file namespace
local fileNamespaces = {
"[Ff]ile",
"[Ii]mage"
}
-- The module keeps all inline templates except these ones
local unwantedInlineTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- The module removes all block templates except these ones
local wantedBlockTemplates = {
"[Hh]istorical populations"
}
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif options.keepTables and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs
if options.keepRefs == '1' then keepRefs = true else keepRefs = false end
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
f16jq11artt2lcseaqxbuq7kwheo14d
797318
797317
2020-02-05T11:30:57Z
en>Certes
0
Remove lowercase {{notetag}} to fix [[Integer]] in [[Portal:Arithmetic]]
797318
Scribunto
text/plain
-- Local aliases of the file namespace
local fileNamespaces = {
"[Ff]ile",
"[Ii]mage"
}
-- The module keeps all inline templates except these ones
local unwantedInlineTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "[Nn]ote[Tt]ag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- The module removes all block templates except these ones
local wantedBlockTemplates = {
"[Hh]istorical populations"
}
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif options.keepTables and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs
if options.keepRefs == '1' then keepRefs = true else keepRefs = false end
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
1kx6jdeac2u338ygixqrm45d29f3dyp
797319
797318
2020-02-13T12:12:58Z
en>Certes
0
Omit unnecessary list if showing all excerpts
797319
Scribunto
text/plain
-- Local aliases of the file namespace
local fileNamespaces = {
"[Ff]ile",
"[Ii]mage"
}
-- The module keeps all inline templates except these ones
local unwantedInlineTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "[Nn]ote[Tt]ag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- The module removes all block templates except these ones
local wantedBlockTemplates = {
"[Hh]istorical populations"
}
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif options.keepTables and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs
if options.keepRefs == '1' then keepRefs = true else keepRefs = false end
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
pjs5jvuatg5t94omdq0wafabypeiiso
797320
797319
2020-04-01T17:34:48Z
en>Sophivorus
0
Check that the section title is not empty
797320
Scribunto
text/plain
-- Local aliases of the file namespace
local fileNamespaces = {
"[Ff]ile",
"[Ii]mage"
}
-- The module keeps all inline templates except these ones
local unwantedInlineTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "[Nn]ote[Tt]ag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- The module removes all block templates except these ones
local wantedBlockTemplates = {
"[Hh]istorical populations"
}
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif options.keepTables and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs
if options.keepRefs == '1' then keepRefs = true else keepRefs = false end
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
o96m8buf6soqw2uvjuaes48vbij1m82
797321
797320
2020-04-01T20:59:27Z
en>Sophivorus
0
Optionally add articles with broken excerpts to a tracking category
797321
Scribunto
text/plain
-- Local aliases of the file namespace
local fileNamespaces = {
"[Ff]ile",
"[Ii]mage"
}
-- Local category to track content pages with broken excerpts (may be empty, don't include the "Category:" prefix)
local brokenCategory = "Articles with broken excerpts"
-- The module keeps all inline templates except these ones
local unwantedInlineTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "[Nn]ote[Tt]ag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- The module removes all block templates except these ones
local wantedBlockTemplates = {
"[Hh]istorical populations"
}
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif options.keepTables and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs
if options.keepRefs == '1' then keepRefs = true else keepRefs = false end
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
if text == "" and brokenCategory and brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
en3hzq8w98iqp64ras69l4o9l45emjm
797322
797321
2020-04-03T22:12:36Z
en>Sophivorus
0
Add option to remove all bold text from excerpt
797322
Scribunto
text/plain
-- Local aliases of the file namespace
local fileNamespaces = {
"[Ff]ile",
"[Ii]mage"
}
-- Local category to track content pages with broken excerpts (may be empty, don't include the "Category:" prefix)
local brokenCategory = "Articles with broken excerpts"
-- The module keeps all inline templates except these ones
local unwantedInlineTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "[Nn]ote[Tt]ag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- The module removes all block templates except these ones
local wantedBlockTemplates = {
"[Hh]istorical populations"
}
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = mw.ustring.match(text, "|%s*[^=|]*[Cc][Aa][Pp][Tt][Ii][Oo][Nn][^=|]*%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif options.keepTables and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs
if options.keepRefs == '1' then keepRefs = true else keepRefs = false end
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if pageopts.nobold then text = mw.ustring.gsub(text, "'''", "") end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
if text == "" and brokenCategory and brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
e7wjsa3vx6z7yqkbu8uabrov9seo48i
797323
797322
2020-04-04T19:38:50Z
en>Sophivorus
0
Update to latest sandbox version
797323
Scribunto
text/plain
-- Local aliases of the file namespace
local fileNamespaces = {
"[Ff]ile",
"[Ii]mage"
}
local captionParams = {
"[^=|]*[Cc]aption[^=|]*",
"[^=|]*[Ll]egend[^=|]*"
}
-- Local category to track content pages with broken excerpts (may be empty, don't include the "Category:" prefix)
local brokenCategory = "Articles with broken excerpts"
-- The module keeps all inline templates except these ones
local unwantedInlineTemplates = {"[Ee]fn", "[Ee]fn%-[lu][arg]", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "[Nn]ote[Tt]ag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
"[CcDd]n", "[Cc]itation[%- _]needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article",
"[Dd]ISPLAYTITLE", "[Ss]hort[ _]+description", "[Cc]itation", "[Cc]ite[%- _]+[%w_%s]-", "[Cc]oor[%w_%s]-",
"[Uu]?n?[Rr]eliable source[%?%w_%s]-", "[Rr]s%??", "[Vv]c", "[Vv]erify credibility", "[Bb]y[ _]*[Ww]ho[m]*%??", "[Ww]ikisource[ -_]*multi", "[Ii]nflation[ _/-]*[Ff]n",
"[Bb]iblesource",
-- aliases for Clarification needed
"[Cc]f[ny]", "[Cc]larification[ _]+inline", "[Cc]larification[%- _]*needed", "[Cc]larification", "[Cc]larify%-inline", "[Cc]larify%-?me",
"[Cc]larify[ _]+inline", "[Cc]larify", "[Cc]LARIFY", "[Cc]onfusing%-inline", "[Cc]onfusing%-short", "[Ee]xplainme", "[Hh]uh[ _]*%??", "[Ww]hat%?",
"[Ii]nline[ _]+[Uu]nclear", "[Ii]n[ _]+what[ _]+sense", "[Oo]bscure", "[Pp]lease[ _]+clarify", "[Uu]nclear[ _]+inline", "[Ww]hat's[ _]+this%?",
"[Gg]eoQuelle", "[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]", "[Ii]PA needed",
-- aliases for Clarification needed lead
"[Cc]itation needed %(?lea?de?%)?", "[Cc]nl", "[Ff]act %(?lea?de?%)?", "[Ll]ead citation needed", "[Nn]ot in body", "[Nn]ot verified in body",
-- Primary source etc.
"[Pp]s[ci]", "[Nn]psn", "[Nn]on%-primary[ _]+source[ _]+needed", "[Ss]elf%-published[%w_%s]-", "[Uu]ser%-generated[%w_%s]-",
"[Pp]rimary source[%w_%s]-", "[Ss]econdary source[%w_%s]-", "[Tt]ertiary source[%w_%s]-", "[Tt]hird%-party[%w_%s]-",
-- aliases for Disambiguation (page) and similar
"[Bb]egriffsklärung", "[Dd][Aa][Bb]", "[Dd]big", "[%w_%s]-%f[%w][Dd]isam[%w_%s]-", "[Hh][Nn][Dd][Ii][Ss]",
-- aliases for Failed verification
"[Bb]adref", "[Ff]aile?[ds] ?[rv][%w_%s]-", "[Ff][Vv]", "[Nn][Ii]?[Cc][Gg]", "[Nn]ot ?in ?[crs][%w_%s]-", "[Nn]ot specifically in source",
"[Vv]erification[%- _]failed",
-- aliases for When
"[Aa]s[ _]+of[ _]+when%??", "[Aa]s[ _%-]+of%??", "[Cc]larify date", "[Dd]ate[ _]*needed", "[Nn]eeds?[ _]+date", "[Rr]ecently", "[Ss]ince[ _]+when%??",
"[Ww]HEN", "[Ww]hen%??",
-- aliases for Update
"[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?", "[Uu]pdate", "[Uu]pdate[ _]+sect", "[Uu]pdate[ _]+Watch",
-- aliases for Pronunciation needed
"[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?", "[Pp]ronounce", "[Rr]equested[%- _]*pronunciation", "[Rr]e?q?pron", "[Nn]eeds[%- _]*pronunciation",
-- Chart, including Chart/start etc.
"[Cc]hart", "[Cc]hart/[%w_%s]-",
-- Cref and others
"[Cc]ref2?", "[Cc]note",
-- Explain and others
"[Ee]xplain", "[Ff]urther[ ]*explanation[ ]*needed", "[Ee]laboration[ ]*needed", "[Ee]xplanation[ ]*needed",
-- TOC templates
"[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*", "[Tt][Oo][Cc]", "09[Aa][Zz]", "[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]", "[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]", "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]",
"DEFAULTSORT:.-",
"[Oo]ne[ _]+source"
}
-- The module removes all block templates except these ones
local wantedBlockTemplates = {
"[Hh]istorical populations"
}
local p = {}
local mRedirect = require('Module:Redirect')
-- Get a redirect target (or nil if not a redirect) without using the expensive title object property .isRedirect
local function getRedirectTarget(titleObject)
local content = titleObject:getContent()
if not content then return nil end
return mRedirect.getTargetFromText(content)
end
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local redir = getRedirectTarget(title)
if redir then title = mw.title.new(redir) end
return title:getContent(), redir or title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = matchany(text, "|%s*", captionParams, "%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif options.keepTables and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, leadOnly, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if leadOnly then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if it's the start of the article (blank lead)
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not keepRefs then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs
if options.keepRefs == '1' then keepRefs = true else keepRefs = false end
text = cleanupText(text, true, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if pageopts.nobold then text = mw.ustring.gsub(text, "'''", "") end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
if text == "" and brokenCategory and brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, leadOnly, keepRefs) return cleanupText(text, leadOnly, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
efvjqblkskl9ciphr8rj613q6uimmws
797324
797323
2020-04-08T01:56:14Z
en>Sophivorus
0
Update to latest sandbox version
797324
Scribunto
text/plain
-- Get localization data
local d = require("Module:Excerpt/l10n")
local p = {}
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Helper function to test for falsy values
local function falsy( value )
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return true
end
return false
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = matchany(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif not falsy(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, keepSubsections, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if falsy(keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if falsy(keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
local section
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn
pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs = options.keepRefs
local keepSubsections = options.keepSubsections
text = cleanupText(text, keepSubsections, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if not falsy(pageopts.nobold) then text = mw.ustring.gsub(text, "'''", "") end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. d.brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, keepSubsections, keepRefs) return cleanupText(text, keepSubsections, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
pafcufkpebw9iyu6yvvajw88favptb3
797325
797324
2020-04-23T03:55:02Z
en>JJMC89
0
Changed protection level for "[[Module:Excerpt]]": [[WP:High-risk templates|Highly visible module]], matching [[Template:Excerpt]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
797324
Scribunto
text/plain
-- Get localization data
local d = require("Module:Excerpt/l10n")
local p = {}
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Helper function to test for falsy values
local function falsy( value )
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return true
end
return false
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = matchany(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif not falsy(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, keepSubsections, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if falsy(keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if falsy(keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
local section
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn
pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs = options.keepRefs
local keepSubsections = options.keepSubsections
text = cleanupText(text, keepSubsections, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if not falsy(pageopts.nobold) then text = mw.ustring.gsub(text, "'''", "") end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. d.brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, keepSubsections, keepRefs) return cleanupText(text, keepSubsections, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
pafcufkpebw9iyu6yvvajw88favptb3
797326
797325
2020-04-23T18:08:04Z
en>Ahecht
0
Implement {{[[Template:Excerpt|Excerpt]]}} with new excerpt() function. Allows articles to reduce [[WP:PEIS]] by calling module directly.
797326
Scribunto
text/plain
-- Get localization data
local d = require("Module:Excerpt/l10n")
local p = {}
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Helper function to test for falsy values
local function falsy( value )
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return true
end
return false
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = matchany(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif not falsy(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, keepSubsections, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if falsy(keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if falsy(keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
local section
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn
pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs = options.keepRefs
local keepSubsections = options.keepSubsections
text = cleanupText(text, keepSubsections, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if not falsy(pageopts.nobold) then text = mw.ustring.gsub(text, "'''", "") end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. d.brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
local function is(v)
return (v or '') ~= ''
end
local function excerpt(frame) -- Replicate {{Excerpt}} entirely in Lua for reduced Post-expand include size
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local tag = is(args.tag) and args.tag or 'div'
local article = is(args.article) and args.article or args[1] or '{{{1}}}'
local section = is(args.section) and args.section or args[2]
local output = {}
output[1] = frame:extensionTag{ name = 'templatestyles', args = {src='Excerpt/styles.css'} }
output[2] = '<' .. tag .. ' class="excerpt-block">'
output[3] = is(args.indicator) and ('<' .. tag .. ' class="excerpt-indicator">') or ''
if is(args.nohat) then
output[4] = ''
else
local hatnote = {}
hatnote[1] = 'This' .. (is(args.indicator) and '' or ' section') .. ' is an excerpt from '
hatnote[2] = '[['
hatnote[3] = article .. (is(section) and ('#' .. frame:callParserFunction( 'urlencode', section, 'WIKI' )) or '')
hatnote[4] = '|'
hatnote[5] = article .. (is(section) and (frame:callParserFunction( '#tag:nowiki', ' § ' ) .. section) or '')
hatnote[6] = ']]'
hatnote[7] = "''" .. '<span class="mw-editsection-like plainlinks"><span>[ </span>['
local title = mw.title.new(article) or mw.title.getCurrentTitle()
hatnote[8] = title:fullUrl('action=edit') .. ' edit'
hatnote[9] = ']<span> ]</span></span>' .. "''"
output[4] = require('Module:Hatnote')._hatnote(table.concat(hatnote), {selfref=true})
end
output[5] = '<' .. tag .. ' class="excerpt">\n'
if article ~= '{{{1}}}' then
if is(args.fragment) then
output[6] = frame:callParserFunction( '#lst', article, args.fragment) or ''
else
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args.paragraphs or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args.files or "1") -- parse file numbers
options.nobold=1
options.keepTables = is(args.tables) and args.tables or 1
options.keepRefs = is(args.references) and args.references or 1
options.keepSubsections = is(args.subsections) and args.subsections or ""
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local pagenames = { (article .. '#' .. (section or '')) }
local text = main(pagenames, options)
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
output[6] = "[[Category:" .. d.brokenCategory .. "]]"
else
output[6] = frame:preprocess(text)
end
end
else
output[6] = err("No article provided")
end
output[7] = '</' .. tag .. '>'
output[8] = is(args.indicator) and ('</' .. tag .. '>') or ''
output[9] = '</' .. tag .. '>'
output[10] = mw.title.getCurrentTitle().isContentPage and '[[Category:Articles with excerpts]]' or ''
return table.concat(output)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
function p.excerpt(frame) return excerpt(frame) end -- {{Excerpt}} transcludes part of an article into another article
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, keepSubsections, keepRefs) return cleanupText(text, keepSubsections, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
ned6juzorez6fkpissp3pxugxq8yxc3
797327
797326
2020-04-23T20:02:55Z
en>Ahecht
0
better error handling
797327
Scribunto
text/plain
-- Get localization data
local d = require("Module:Excerpt/l10n")
local p = {}
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Helper function to test for falsy values
local function falsy( value )
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return true
end
return false
end
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function striptemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchany(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noref = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noref = mw.ustring.gsub(noref, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noref = mw.ustring.sub(noref, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noref, 3), "%b{}", striptemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noref = mw.ustring.gsub(noref, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noref = mw.ustring.gsub(noref, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noref ~= t then return noref end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Check image for suitability
local function checkimage(image)
local page = matchany(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local desc, rtitle = getContent(page) -- get file description and title after following any redirect
if desc and desc ~= "" then -- found description on local wiki
if mw.ustring.match(desc, "[Nn]on%-free") then return false end
desc = mw.ustring.gsub(desc, "%b{}", striptemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not rtitle then
return false
else
-- try commons
desc = "{{" .. rtitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
desc = frame:preprocess(desc)
return ( desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchany(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaption(caption)
if not caption then return nil end
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local capture_from = 1
while capture_from < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", capture_from)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgname = mw.ustring.lower(argname)
if mw.ustring.find(lcArgname, "caption")
or mw.ustring.find(lcArgname, "size")
or mw.ustring.find(lcArgname, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", capture_from)
if image then
hasImages = true
images[position] = image
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", capture_from)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
capture_from = 1
while capture_from < mw.ustring.len(text) do
local position, caption = matchany(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", capture_from)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parsecaption(caption)
end
end
end
capture_from = position
else
capture_from = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookfrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local len = mw.ustring.len(altText)
local aftertext = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookfrom) or len+1,
mw.ustring.match(altText, "()|", lookfrom) or len+1)
altText = mw.ustring.sub(altText, 1, aftertext-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImagemap(imagemap)
local image = matchany(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileargs)
if fileargs then
for _, filearg in pairs(mw.text.split(fileargs, "|")) do -- handle fileargs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allparas = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberflags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allparas = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberflags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileargs = options.fileargs and mw.text.trim(options.fileargs)
if fileargs == '' then fileargs = nil end
local leadstart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local filetext = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileargs)
if checkimage(f) then filetext = filetext .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadstart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchany(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadstart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchany(token, "{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif not falsy(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argimage(token) or {}
if not images then
local image = parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
end
end
else -- the next token in text is not a template
token = parseimage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileargs)
filetext = filetext .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
local isHatnote = not(leadstart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return filetext, text
end
local function cleanupText(text, keepSubsections, keepRefs)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if falsy(keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if falsy(keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImagemap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getsection(text, section, mainonly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextsection
if mainonly then
nextsection = "\n==.*" -- Main part of section terminates at any level of header
else
nextsection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextsection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixtags(text, tag)
local startcount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startcount = startcount + 1 end
local endcount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endcount = endcount + 1 end
if startcount > endcount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endcount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endcount > startcount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endcount - startcount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pagenames, options)
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotopt
local pageoptstr
local section
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotopt, pageoptstr = mw.ustring.match(pagename, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pagename = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pagename, gotopt, pageoptstr = mw.ustring.match(pagename, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pagename and pagename ~= "" then
local pn
pn, section = mw.ustring.match(pagename, "(.-)#(.*)")
pagename = pn or pagename
text, normalisedPagename = getContent(pagename)
if not normalisedPagename then
return err("No title for page name " .. pagename)
else
pagename = normalisedPagename
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pagename, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getsection(text, section) end
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
local keepRefs = options.keepRefs
local keepSubsections = options.keepSubsections
text = cleanupText(text, keepSubsections, keepRefs)
local pageopts = {} -- pageopts (even if value is "") have priority over global options
for k, v in pairs(options) do pageopts[k] = v end
if gotopt and gotopt ~= "" then
for _, t in pairs(mw.text.split(pageoptstr, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageopts[k] = v
end
pageopts.paraflags = numberflags(pageopts["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageopts.fileflags = numberflags(pageopts["files"] or "") -- parse file numbers
if pageopts.more and pageopts.more == "" then pageopts.more = "Read more..." end -- more= is short for this default text
end
local filetext
filetext, text = parse(text, pageopts)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if not falsy(pageopts.nobold) then text = mw.ustring.gsub(text, "'''", "") end
text = filetext .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixtags(text, "div")
if pageopts.more then text = text .. " '''[[" .. pagename .. "|" .. pageopts.more .. "]]'''" end -- wikilink to article for more info
if pageopts.list and not pageopts.showall then -- add a collapsed list of pages which might appear
local listtext = pageopts.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pagenames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, func)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articlecount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articlecount < 1 and not (func == "selected" and args[func] and args[args[func]]) then
return err("No articles provided")
end
local pagenames = {}
if func == "lead" then
pagenames = { args[1] }
elseif func == "linked" or func == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getsection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if func == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pagenames, p) end
end
elseif func == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
elseif func == "selected" then
local articlekey = args[func]
if tonumber(articlekey) then -- normalise article number into the range 1..#args
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
pagenames = { args[articlekey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pagenames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pagenames, options)
end
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. d.brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
local function is(v)
return (v or '') ~= ''
end
local function excerpt(frame) -- Replicate {{Excerpt}} entirely in Lua for reduced Post-expand include size
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local tag = is(args.tag) and args.tag or 'div'
local article = is(args.article) and args.article or args[1] or '{{{1}}}'
local section = is(args.section) and args.section or args[2]
local output = {}
output[1] = frame:extensionTag{ name = 'templatestyles', args = {src='Excerpt/styles.css'} }
output[2] = '<' .. tag .. ' class="excerpt-block">'
output[3] = is(args.indicator) and ('<' .. tag .. ' class="excerpt-indicator">') or ''
if is(args.nohat) then
output[4] = ''
else
local hatnote = {}
hatnote[1] = 'This' .. (is(args.indicator) and '' or ' section') .. ' is an excerpt from '
hatnote[2] = '[['
hatnote[3] = article .. (is(section) and ('#' .. frame:callParserFunction( 'urlencode', section, 'WIKI' )) or '')
hatnote[4] = '|'
hatnote[5] = article .. (is(section) and (frame:callParserFunction( '#tag:nowiki', ' § ' ) .. section) or '')
hatnote[6] = ']]'
hatnote[7] = "''" .. '<span class="mw-editsection-like plainlinks"><span>[ </span>['
local title = mw.title.new(article) or mw.title.getCurrentTitle()
hatnote[8] = title:fullUrl('action=edit') .. ' edit'
hatnote[9] = ']<span> ]</span></span>' .. "''"
output[4] = require('Module:Hatnote')._hatnote(table.concat(hatnote), {selfref=true}) or ''
end
output[5] = '<' .. tag .. ' class="excerpt">\n'
if article ~= '{{{1}}}' then
if is(args.fragment) then
output[6] = frame:callParserFunction( '#lst', article, args.fragment) or err("Error transcluding text")
else
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberflags(args.paragraphs or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args.files or "1") -- parse file numbers
options.nobold=1
options.keepTables = is(args.tables) and args.tables or 1
options.keepRefs = is(args.references) and args.references or 1
options.keepSubsections = is(args.subsections) and args.subsections or ""
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local pagenames = { (article .. '#' .. (section or '')) }
local text = main(pagenames, options)
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
output[6] = "[[Category:" .. d.brokenCategory .. "]]"
else
output[6] = frame:preprocess(text) or err("Error processing text")
end
end
else
output[6] = err("No article provided")
end
output[7] = '</' .. tag .. '>'
output[8] = is(args.indicator) and ('</' .. tag .. '>') or ''
output[9] = '</' .. tag .. '>'
output[10] = mw.title.getCurrentTitle().isContentPage and '[[Category:Articles with excerpts]]' or ''
return table.concat(output)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
function p.excerpt(frame) return excerpt(frame) end -- {{Excerpt}} transcludes part of an article into another article
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getsection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argimage(text) end
function p.checkimage(image) return checkimage(image) end
function p.parseimage(text, start) return parseimage(text, start) end
function p.cleanupText(text, keepSubsections, keepRefs) return cleanupText(text, keepSubsections, keepRefs) end
function p.main(pagenames, options) return main(pagenames, options) end
function p.numberflags(str) return numberflags(str) end
return p
9s80nrungsb0i5693mptniwfbchnvj2
797328
797327
2020-04-29T22:49:51Z
en>Sophivorus
0
Update to latest sandbox version. Integrate #lst into the module. Change function and variable names to camelCase. Move localization submodule from /l10n to /i18n. Simplify the excerpt function.
797328
Scribunto
text/plain
-- Get localized data
local d = require("Module:Excerpt/i18n")
local p = {}
-- Helper function to debug
-- Returns blank text or an error message if requested
local errors
local function err(msg,a,b)
local text = mw.ustring.format(d.error[msg] or msg or '',a,b)
if errors then error(text, 2) end
return ""
end
-- Helper function to test for truthy and falsy values
local function is(value)
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return false
end
return true
end
-- Helper function to match from a list regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
local function matchAny(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function stripTemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchAny(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noRef = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noRef = mw.ustring.gsub(noRef, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noRef = mw.ustring.sub(noRef, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noRef, 3), "%b{}", stripTemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noRef = mw.ustring.gsub(noRef, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noRef = mw.ustring.gsub(noRef, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noRef ~= t then return noRef end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Check image for suitability
local function checkImage(image)
local page = matchAny(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchAny(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local fileDescription, fileTitle = getContent(page) -- get file description and title after following any redirect
if fileDescription and fileDescription ~= "" then -- found description on local wiki
if mw.ustring.match(fileDescription, "[Nn]on%-free") then return false end
fileDescription = mw.ustring.gsub(fileDescription, "%b{}", stripTemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not fileTitle then
return false
else
-- try commons
fileDescription = "{{" .. fileTitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
fileDescription = frame:preprocess(fileDescription)
return ( fileDescription and fileDescription ~= "" and not mw.ustring.match(fileDescription, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseImage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchAny(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parseCaption(caption)
if not caption then return nil end
local length = mw.ustring.len(caption)
local position = 1
while position <= length do
local linkStart, linkEnd = mw.ustring.find(caption, "%b[]", position)
linkStart = linkStart or length + 1 -- avoid comparison with nil when no link
local templateStart, templateEnd = mw.ustring.find(caption, "%b{}", position)
templateStart = templateStart or length + 1 -- avoid comparison with nil when no template
local argEnd = mw.ustring.find(caption, "[|}]", position) or length + 1
if linkStart < templateStart and linkStart < argEnd then
position = linkEnd + 1 -- skip wikilink
elseif templateStart < argEnd then
position = templateEnd + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argEnd - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argImage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", captureFrom)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgName = mw.ustring.lower(argname)
if mw.ustring.find(lcArgName, "caption")
or mw.ustring.find(lcArgName, "size")
or mw.ustring.find(lcArgName, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", captureFrom)
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", captureFrom)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, caption = matchAny(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", captureFrom)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parseCaption(caption)
end
end
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookFrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local length = mw.ustring.len(altText)
local afterText = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookFrom) or length+1,
mw.ustring.match(altText, "()|", lookFrom) or length+1)
altText = mw.ustring.sub(altText, 1, afterText-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseImage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchAny(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImageMap(imagemap)
local image = matchAny(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberFlags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileArgs)
if fileArgs then
for _, filearg in pairs(mw.text.split(fileArgs, "|")) do -- handle fileArgs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allParagraphs = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberFlags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allParagraphs = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allParagraphs = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberFlags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileArgs = options.fileargs and mw.text.trim(options.fileargs)
if fileArgs == '' then fileArgs = nil end
local leadStart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local fileText = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileArgs)
if checkImage(f) then fileText = fileText .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadStart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchAny(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadStart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchAny(token, "{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif is(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argImage(token) or {}
if not images then
local image = parseImage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkImage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
end
end
else -- the next token in text is not a template
token = parseImage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkImage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterEnd = mw.ustring.len(text) + 1
local blankPosition = mw.ustring.find(text, "\n%s*\n") or afterEnd -- position of next paragraph delimiter (or end of text)
local endPosition = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterEnd,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterEnd,
blankPosition)
token = mw.ustring.sub(text, 1, endPosition-1)
if blankPosition < afterEnd and blankPosition == endPosition then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankPosition)
end
local isHatnote = not(leadStart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadStart = leadStart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allParagraphs or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return fileText, text
end
local function cleanupText(text, options)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not is(options.keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
if not is(options.keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", stripTemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImageMap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getSection(text, section, mainOnly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextSection
if mainOnly then
nextSection = "\n==.*" -- Main part of section terminates at any level of header
else
nextSection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextSection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixTags(text, tag)
local startCount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startCount = startCount + 1 end
local endCount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endCount = endCount + 1 end
if startCount > endCount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endCount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endCount > startCount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endCount - startCount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pageNames, options)
if not pageNames or #pageNames < 1 then return err("No page names given") end
local pageName
local text
local pageCount = #pageNames
local firstPage = pageNames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotOptions
local pageOptionsString
local section
-- read the page, or a random one if multiple pages were provided
if pageCount > 1 then math.randomseed(os.time()) end
while not text and pageCount > 0 do
local pageNumber = 1
if pageCount > 1 then pageNumber = math.random(pageCount) end -- pick a random title
pageName = pageNames[pageNumber]
if pageName and pageName ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotOptions, pageOptionsString = mw.ustring.match(pageName, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pageName = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pageName, gotOptions, pageOptionsString = mw.ustring.match(pageName, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pageName and pageName ~= "" then
local pn
pn, section = mw.ustring.match(pageName, "(.-)#(.*)")
pageName = pn or pageName
text, normalisedPageName = getContent(pageName)
if is(options.fragment) then
local frame = mw.getCurrentFrame()
text = frame:callParserFunction('#lst', normalisedPageName, options.fragment)
end
if not normalisedPageName then
return err("No title for page name " .. pageName)
else
pageName = normalisedPageName
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pageName, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getSection(text, section) end
end
end
if not text then table.remove(pageNames, pageNumber) end -- this one didn't work; try another
pageCount = pageCount - 1 -- ensure that we exit the loop after at most #pageNames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstPage) end
text = cleanupText(text, options)
local pageOptions = {} -- pageOptions (even if value is "") have priority over global options
for k, v in pairs(options) do pageOptions[k] = v end
if gotOptions and gotOptions ~= "" then
for _, t in pairs(mw.text.split(pageOptionsString, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageOptions[k] = v
end
pageOptions.paraflags = numberFlags(pageOptions["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageOptions.fileflags = numberFlags(pageOptions["files"] or "") -- parse file numbers
if pageOptions.more and pageOptions.more == "" then pageOptions.more = "Read more..." end -- more= is short for this default text
end
local fileText
fileText, text = parse(text, pageOptions)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pageName) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pageName) .. "'''", 1, true) -- plain search: special characters in pageName represent themselves
if pos then
local len = mw.ustring.len(pageName)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pageName .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pageName|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if is(pageOptions.nobold) then text = mw.ustring.gsub(text, "'''", "") end
text = fileText .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixTags(text, "div")
if pageOptions.more then text = text .. " '''[[" .. pageName .. "|" .. pageOptions.more .. "]]'''" end -- wikilink to article for more info
if pageOptions.list and not pageOptions.showall then -- add a collapsed list of pages which might appear
local listtext = pageOptions.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pageNames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, template)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articleCount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articleCount < 1 and not (template == "selected" and args[template] and args[args[template]]) then
return err("No articles provided")
end
local pageNames = {}
if template == "lead" then
pageNames = { args[1] }
elseif template == "linked" or template == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getSection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if template == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pageNames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pageNames, p) end
end
elseif template == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pageNames, p) end
end
elseif template == "selected" then
local articleKey = args[template]
if tonumber(articleKey) then -- normalise article number into the range 1..#args
articleKey = articleKey % articleCount
if articleKey == 0 then articleKey = articleCount end
end
pageNames = { args[articleKey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberFlags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberFlags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pageNames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pageNames, options)
end
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. d.brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Replicate {{Excerpt}} entirely in Lua for reduced Post-expand include size
local function excerpt(frame)
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local tag = is(args.tag) and args.tag or 'div'
local article = is(args.article) and args.article or args[1] or '{{{1}}}'
local section = is(args.section) and args.section or args[2]
local output = {}
output[1] = frame:extensionTag{ name = 'templatestyles', args = {src='Excerpt/styles.css'} }
output[2] = '<' .. tag .. ' class="excerpt-block">'
output[3] = is(args.indicator) and ('<' .. tag .. ' class="excerpt-indicator">') or ''
if is(args.nohat) then
output[4] = ''
else
local hatnote = {}
hatnote[1] = 'This' .. (is(args.indicator) and '' or ' section') .. ' is an excerpt from '
hatnote[2] = '[['
hatnote[3] = article .. (is(section) and ('#' .. frame:callParserFunction( 'urlencode', section, 'WIKI' )) or '')
hatnote[4] = '|'
hatnote[5] = article .. (is(section) and (frame:callParserFunction( '#tag:nowiki', ' § ' ) .. section) or '')
hatnote[6] = ']]'
hatnote[7] = "''" .. '<span class="mw-editsection-like plainlinks"><span>[ </span>['
local title = mw.title.new(article) or mw.title.getCurrentTitle()
hatnote[8] = title:fullUrl('action=edit') .. ' edit'
hatnote[9] = ']<span> ]</span></span>' .. "''"
output[4] = require('Module:Hatnote')._hatnote(table.concat(hatnote), {selfref=true}) or err("Error generating hatnote")
end
output[5] = '<' .. tag .. ' class="excerpt">\n'
if article ~= '{{{1}}}' then
local options = args -- turn template arguments into module options
options.paraflags = args.paragraphs
options.fileflags = args.files or 1
options.nobold = 1
options.fragment = args.fragment
options.keepTables = args.tables or 1
options.keepRefs = args.references or 1
options.keepSubsections = args.subsections
local pageNames = { (article .. '#' .. (section or '')) }
local text = main(pageNames, options)
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
output[6] = "[[Category:" .. d.brokenCategory .. "]]"
else
output[6] = frame:preprocess(text) or err("Error processing text")
end
else
output[6] = err("No article provided")
end
output[7] = '</' .. tag .. '>'
output[8] = is(args.indicator) and ('</' .. tag .. '>') or ''
output[9] = '</' .. tag .. '>'
output[10] = mw.title.getCurrentTitle().isContentPage and '[[Category:Articles with excerpts]]' or ''
return table.concat(output)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
function p.excerpt(frame) return excerpt(frame) end -- {{Excerpt}} transcludes part of an article into another article
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getSection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argImage(text) end
function p.checkimage(image) return checkImage(image) end
function p.parseimage(text, start) return parseImage(text, start) end
function p.cleanupText(text, options) return cleanupText(text, options) end
function p.main(pageNames, options) return main(pageNames, options) end
function p.numberflags(str) return numberFlags(str) end
return p
tsy316l99ffc2adax9n0zno1dixtsi1
797329
797328
2020-05-13T13:22:38Z
en>Sophivorus
0
Update from latest sandbox version
797329
Scribunto
text/plain
-- Get localized data
local d = require("Module:Excerpt/i18n")
local p = {}
-- Helper function to debug
-- Returns blank text or an error message if requested
local errors
local function err(msg, a, b)
local text = mw.ustring.format(d.error[msg] or msg or "", a, b)
if errors then error(text, 2) end
return ""
end
-- Helper function to test for truthy and falsy values
local function is(value)
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return false
end
return true
end
-- Helper function to match from a list regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
local function matchAny(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Helper function to convert imagemaps into standard images
local function convertImageMap(imagemap)
local image = matchAny(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Helper function to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- For example: "1,3-5" to {1=true,2=false,3=true,4=true,5=true}
local function numberFlags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Helper function to convert template arguments into an array of arguments fit for main()
local function parseArgs(frame)
local args = {}
for key, value in pairs(frame:getParent().args) do args[key] = value end
for key, value in pairs(frame.args) do args[key] = value end -- args from a Lua call have priority over parent args from template
args.paraflags = numberFlags(args["paragraphs"] or "") -- parse paragraphs: "1,3-5" to {"1","3-5"}
args.fileflags = numberFlags(args["files"] or "") -- parse file numbers
return args
end
-- Helper function to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function stripTemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchAny(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noRef = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noRef = mw.ustring.gsub(noRef, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noRef = mw.ustring.sub(noRef, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noRef, 3), "%b{}", stripTemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noRef = mw.ustring.gsub(noRef, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noRef = mw.ustring.gsub(noRef, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noRef ~= t then return noRef end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Return the target of the redirect,
-- or the same title if it's not a redirect
-- or nil if the title was not found
local function getTarget(page)
local title = mw.title.new(page)
if title then
local target = title.redirectTarget
if target then
return target.prefixedText
end
return title.prefixedText
end
end
-- Check image for suitability
local function checkImage(image)
local page = matchAny(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchAny(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local fileDescription, fileTitle = getContent(page) -- get file description and title after following any redirect
if fileDescription and fileDescription ~= "" then -- found description on local wiki
if mw.ustring.match(fileDescription, "[Nn]on%-free") then return false end
fileDescription = mw.ustring.gsub(fileDescription, "%b{}", stripTemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not fileTitle then
return false
else
-- try commons
fileDescription = "{{" .. fileTitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
fileDescription = frame:preprocess(fileDescription)
return ( fileDescription and fileDescription ~= "" and not mw.ustring.match(fileDescription, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseImage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchAny(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parseCaption(caption)
if not caption then return nil end
local length = mw.ustring.len(caption)
local position = 1
while position <= length do
local linkStart, linkEnd = mw.ustring.find(caption, "%b[]", position)
linkStart = linkStart or length + 1 -- avoid comparison with nil when no link
local templateStart, templateEnd = mw.ustring.find(caption, "%b{}", position)
templateStart = templateStart or length + 1 -- avoid comparison with nil when no template
local argEnd = mw.ustring.find(caption, "[|}]", position) or length + 1
if linkStart < templateStart and linkStart < argEnd then
position = linkEnd + 1 -- skip wikilink
elseif templateStart < argEnd then
position = templateEnd + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argEnd - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argImage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", captureFrom)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgName = mw.ustring.lower(argname)
if mw.ustring.find(lcArgName, "caption")
or mw.ustring.find(lcArgName, "size")
or mw.ustring.find(lcArgName, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", captureFrom)
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", captureFrom)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, caption = matchAny(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", captureFrom)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parseCaption(caption)
end
end
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookFrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local length = mw.ustring.len(altText)
local afterText = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookFrom) or length+1,
mw.ustring.match(altText, "()|", lookFrom) or length+1)
altText = mw.ustring.sub(altText, 1, afterText-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseImage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchAny(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
local function modifyImage(image, fileArgs)
if fileArgs then
for _, filearg in pairs(mw.text.split(fileArgs, "|")) do -- handle fileArgs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(d.imageParams) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allParagraphs = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberFlags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allParagraphs = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allParagraphs = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberFlags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileArgs = options.fileargs and mw.text.trim(options.fileargs)
if fileArgs == '' then fileArgs = nil end
local leadStart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local fileText = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileArgs)
if checkImage(f) then fileText = fileText .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadStart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchAny(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadStart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchAny(token, "{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif is(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argImage(token) or {}
if not images then
local image = parseImage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkImage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
end
end
else -- the next token in text is not a template
token = parseImage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkImage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterEnd = mw.ustring.len(text) + 1
local blankPosition = mw.ustring.find(text, "\n%s*\n") or afterEnd -- position of next paragraph delimiter (or end of text)
local endPosition = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterEnd,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterEnd,
blankPosition)
token = mw.ustring.sub(text, 1, endPosition-1)
if blankPosition < afterEnd and blankPosition == endPosition then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankPosition)
end
local isHatnote = not(leadStart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadStart = leadStart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allParagraphs or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return fileText, text
end
local function cleanupText(text, options)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not is(options.keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
if not is(options.keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", stripTemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImageMap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getSection(text, section, mainOnly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextSection
if mainOnly then
nextSection = "\n==.*" -- Main part of section terminates at any level of header
else
nextSection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextSection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixTags(text, tag)
local startCount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startCount = startCount + 1 end
local endCount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endCount = endCount + 1 end
if startCount > endCount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endCount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endCount > startCount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endCount - startCount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pageNames, options)
if not pageNames or #pageNames < 1 then return err("pageNames") end
local pageName
local text
local pageCount = #pageNames
local firstPage = pageNames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotOptions
local pageOptionsString
local section
-- read the page, or a random one if multiple pages were provided
if pageCount > 1 then math.randomseed(os.time()) end
while not text and pageCount > 0 do
local pageNumber = 1
if pageCount > 1 then pageNumber = math.random(pageCount) end -- pick a random title
pageName = pageNames[pageNumber]
if pageName and pageName ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotOptions, pageOptionsString = mw.ustring.match(pageName, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pageName = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pageName, gotOptions, pageOptionsString = mw.ustring.match(pageName, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pageName and pageName ~= "" then
local pn
pn, section = mw.ustring.match(pageName, "(.-)#(.*)")
pageName = pn or pageName
text, normalisedPageName = getContent(pageName)
if is(options.fragment) then
local frame = mw.getCurrentFrame()
text = frame:callParserFunction('#lst', normalisedPageName, options.fragment)
end
if not normalisedPageName then
return err("noTitle", pageName)
else
pageName = normalisedPageName
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pageName, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getSection(text, section) end
end
end
if not text then table.remove(pageNames, pageNumber) end -- this one didn't work; try another
pageCount = pageCount - 1 -- ensure that we exit the loop after at most #pageNames iterations
end
if not text then return err("firstPage", firstPage) end
text = cleanupText(text, options)
local pageOptions = {} -- pageOptions (even if value is "") have priority over global options
for k, v in pairs(options) do pageOptions[k] = v end
if gotOptions and gotOptions ~= "" then
for _, t in pairs(mw.text.split(pageOptionsString, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageOptions[k] = v
end
pageOptions.paraflags = numberFlags(pageOptions["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageOptions.fileflags = numberFlags(pageOptions["files"] or "") -- parse file numbers
if pageOptions.more and pageOptions.more == "" then pageOptions.more = "Read more..." end -- more= is short for this default text
end
local fileText
fileText, text = parse(text, pageOptions)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find(text, "'''" .. lang:ucfirst(pageName) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pageName) .. "'''", 1, true) -- plain search: special characters in pageName represent themselves
if position then
local length = mw.ustring.len(pageName)
text = mw.ustring.sub(text, 1, position + 2) .. "[[" .. mw.ustring.sub(text, position + 3, position + length + 2) .. "]]" .. mw.ustring.sub(text, position + length + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pageName .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pageName|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if is(pageOptions.nobold) then text = mw.ustring.gsub(text, "'''", "") end
text = fileText .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixTags(text, "div")
if pageOptions.more then text = text .. " '''[[" .. pageName .. "|" .. pageOptions.more .. "]]'''" end -- wikilink to article for more info
if pageOptions.list and not pageOptions.showall then -- add a collapsed list of pages which might appear
local listtext = pageOptions.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pageNames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Main invocation function for templates
local function lead(frame)
local args = parseArgs(frame)
local pageNames = { args[1] }
local text = main(pageNames, args)
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. d.brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Entry points for templates
function p.lead(frame) return lead(frame) end
function p.target(frame) return getTarget(frame.args[1]) end
-- Entry points for other Lua modules
function p.getTarget(page) return getTarget(page) end
function p.getContent(page, frame) return getContent(page, frame) end
function p.getSection(text, section) return getSection(text, section) end
function p.getsection(text, section) return getSection(text, section) end -- Temporary entry point for backwards compatibility
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.parseImage(text, start) return parseImage(text, start) end
function p.parseimage(text, start) return parseImage(text, start) end -- Temporary entry point for backwards compatibility
function p.parseArgs(frame) return parseArgs(frame) end
function p.argImage(text) return argImage(text) end
function p.argimage(text) return argImage(text) end -- Temporary entry point for backwards compatibility
function p.checkImage(image) return checkImage(image) end
function p.checkimage(image) return checkImage(image) end -- Temporary entry point for backwards compatibility
function p.cleanupText(text, options) return cleanupText(text, options) end
function p.main(pageNames, options) return main(pageNames, options) end
function p.err(msg, a, b) return err(msg, a, b) end
function p.is(value) return is(value) end
function p.numberFlags(str) return numberFlags(str) end
function p.numberflags(str) return numberFlags(str) end -- Temporary entry point for backwards compatibility
return p
puserc5ig1ek0mdeydgzpldm4galznr
797330
797329
2020-05-13T13:23:23Z
en>Pppery
0
Please stop misusing your global interface editor access to edit template-protected templates
797330
Scribunto
text/plain
-- Get localized data
local d = require("Module:Excerpt/i18n")
local p = {}
-- Helper function to debug
-- Returns blank text or an error message if requested
local errors
local function err(msg,a,b)
local text = mw.ustring.format(d.error[msg] or msg or '',a,b)
if errors then error(text, 2) end
return ""
end
-- Helper function to test for truthy and falsy values
local function is(value)
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return false
end
return true
end
-- Helper function to match from a list regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
local function matchAny(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function stripTemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchAny(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noRef = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noRef = mw.ustring.gsub(noRef, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noRef = mw.ustring.sub(noRef, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noRef, 3), "%b{}", stripTemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noRef = mw.ustring.gsub(noRef, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noRef = mw.ustring.gsub(noRef, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noRef ~= t then return noRef end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Check image for suitability
local function checkImage(image)
local page = matchAny(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchAny(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local fileDescription, fileTitle = getContent(page) -- get file description and title after following any redirect
if fileDescription and fileDescription ~= "" then -- found description on local wiki
if mw.ustring.match(fileDescription, "[Nn]on%-free") then return false end
fileDescription = mw.ustring.gsub(fileDescription, "%b{}", stripTemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not fileTitle then
return false
else
-- try commons
fileDescription = "{{" .. fileTitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
fileDescription = frame:preprocess(fileDescription)
return ( fileDescription and fileDescription ~= "" and not mw.ustring.match(fileDescription, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseImage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchAny(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parseCaption(caption)
if not caption then return nil end
local length = mw.ustring.len(caption)
local position = 1
while position <= length do
local linkStart, linkEnd = mw.ustring.find(caption, "%b[]", position)
linkStart = linkStart or length + 1 -- avoid comparison with nil when no link
local templateStart, templateEnd = mw.ustring.find(caption, "%b{}", position)
templateStart = templateStart or length + 1 -- avoid comparison with nil when no template
local argEnd = mw.ustring.find(caption, "[|}]", position) or length + 1
if linkStart < templateStart and linkStart < argEnd then
position = linkEnd + 1 -- skip wikilink
elseif templateStart < argEnd then
position = templateEnd + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argEnd - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argImage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", captureFrom)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgName = mw.ustring.lower(argname)
if mw.ustring.find(lcArgName, "caption")
or mw.ustring.find(lcArgName, "size")
or mw.ustring.find(lcArgName, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", captureFrom)
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", captureFrom)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, caption = matchAny(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", captureFrom)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parseCaption(caption)
end
end
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookFrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local length = mw.ustring.len(altText)
local afterText = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookFrom) or length+1,
mw.ustring.match(altText, "()|", lookFrom) or length+1)
altText = mw.ustring.sub(altText, 1, afterText-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseImage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchAny(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImageMap(imagemap)
local image = matchAny(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberFlags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileArgs)
if fileArgs then
for _, filearg in pairs(mw.text.split(fileArgs, "|")) do -- handle fileArgs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allParagraphs = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberFlags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allParagraphs = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allParagraphs = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberFlags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileArgs = options.fileargs and mw.text.trim(options.fileargs)
if fileArgs == '' then fileArgs = nil end
local leadStart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local fileText = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileArgs)
if checkImage(f) then fileText = fileText .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadStart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchAny(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadStart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchAny(token, "{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif is(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argImage(token) or {}
if not images then
local image = parseImage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkImage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
end
end
else -- the next token in text is not a template
token = parseImage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkImage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterEnd = mw.ustring.len(text) + 1
local blankPosition = mw.ustring.find(text, "\n%s*\n") or afterEnd -- position of next paragraph delimiter (or end of text)
local endPosition = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterEnd,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterEnd,
blankPosition)
token = mw.ustring.sub(text, 1, endPosition-1)
if blankPosition < afterEnd and blankPosition == endPosition then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankPosition)
end
local isHatnote = not(leadStart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadStart = leadStart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allParagraphs or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return fileText, text
end
local function cleanupText(text, options)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not is(options.keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
if not is(options.keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", stripTemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImageMap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getSection(text, section, mainOnly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextSection
if mainOnly then
nextSection = "\n==.*" -- Main part of section terminates at any level of header
else
nextSection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextSection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixTags(text, tag)
local startCount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startCount = startCount + 1 end
local endCount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endCount = endCount + 1 end
if startCount > endCount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endCount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endCount > startCount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endCount - startCount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pageNames, options)
if not pageNames or #pageNames < 1 then return err("No page names given") end
local pageName
local text
local pageCount = #pageNames
local firstPage = pageNames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotOptions
local pageOptionsString
local section
-- read the page, or a random one if multiple pages were provided
if pageCount > 1 then math.randomseed(os.time()) end
while not text and pageCount > 0 do
local pageNumber = 1
if pageCount > 1 then pageNumber = math.random(pageCount) end -- pick a random title
pageName = pageNames[pageNumber]
if pageName and pageName ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotOptions, pageOptionsString = mw.ustring.match(pageName, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pageName = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pageName, gotOptions, pageOptionsString = mw.ustring.match(pageName, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pageName and pageName ~= "" then
local pn
pn, section = mw.ustring.match(pageName, "(.-)#(.*)")
pageName = pn or pageName
text, normalisedPageName = getContent(pageName)
if is(options.fragment) then
local frame = mw.getCurrentFrame()
text = frame:callParserFunction('#lst', normalisedPageName, options.fragment)
end
if not normalisedPageName then
return err("No title for page name " .. pageName)
else
pageName = normalisedPageName
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pageName, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getSection(text, section) end
end
end
if not text then table.remove(pageNames, pageNumber) end -- this one didn't work; try another
pageCount = pageCount - 1 -- ensure that we exit the loop after at most #pageNames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstPage) end
text = cleanupText(text, options)
local pageOptions = {} -- pageOptions (even if value is "") have priority over global options
for k, v in pairs(options) do pageOptions[k] = v end
if gotOptions and gotOptions ~= "" then
for _, t in pairs(mw.text.split(pageOptionsString, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageOptions[k] = v
end
pageOptions.paraflags = numberFlags(pageOptions["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageOptions.fileflags = numberFlags(pageOptions["files"] or "") -- parse file numbers
if pageOptions.more and pageOptions.more == "" then pageOptions.more = "Read more..." end -- more= is short for this default text
end
local fileText
fileText, text = parse(text, pageOptions)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pageName) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pageName) .. "'''", 1, true) -- plain search: special characters in pageName represent themselves
if pos then
local len = mw.ustring.len(pageName)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pageName .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pageName|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if is(pageOptions.nobold) then text = mw.ustring.gsub(text, "'''", "") end
text = fileText .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixTags(text, "div")
if pageOptions.more then text = text .. " '''[[" .. pageName .. "|" .. pageOptions.more .. "]]'''" end -- wikilink to article for more info
if pageOptions.list and not pageOptions.showall then -- add a collapsed list of pages which might appear
local listtext = pageOptions.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pageNames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, template)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articleCount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articleCount < 1 and not (template == "selected" and args[template] and args[args[template]]) then
return err("No articles provided")
end
local pageNames = {}
if template == "lead" then
pageNames = { args[1] }
elseif template == "linked" or template == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getSection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if template == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pageNames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pageNames, p) end
end
elseif template == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pageNames, p) end
end
elseif template == "selected" then
local articleKey = args[template]
if tonumber(articleKey) then -- normalise article number into the range 1..#args
articleKey = articleKey % articleCount
if articleKey == 0 then articleKey = articleCount end
end
pageNames = { args[articleKey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberFlags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberFlags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pageNames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pageNames, options)
end
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. d.brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Replicate {{Excerpt}} entirely in Lua for reduced Post-expand include size
local function excerpt(frame)
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local tag = is(args.tag) and args.tag or 'div'
local article = is(args.article) and args.article or args[1] or '{{{1}}}'
local section = is(args.section) and args.section or args[2]
local output = {}
output[1] = frame:extensionTag{ name = 'templatestyles', args = {src='Excerpt/styles.css'} }
output[2] = '<' .. tag .. ' class="excerpt-block">'
output[3] = is(args.indicator) and ('<' .. tag .. ' class="excerpt-indicator">') or ''
if is(args.nohat) then
output[4] = ''
else
local hatnote = {}
hatnote[1] = 'This' .. (is(args.indicator) and '' or ' section') .. ' is an excerpt from '
hatnote[2] = '[['
hatnote[3] = article .. (is(section) and ('#' .. frame:callParserFunction( 'urlencode', section, 'WIKI' )) or '')
hatnote[4] = '|'
hatnote[5] = article .. (is(section) and (frame:callParserFunction( '#tag:nowiki', ' § ' ) .. section) or '')
hatnote[6] = ']]'
hatnote[7] = "''" .. '<span class="mw-editsection-like plainlinks"><span>[ </span>['
local title = mw.title.new(article) or mw.title.getCurrentTitle()
hatnote[8] = title:fullUrl('action=edit') .. ' edit'
hatnote[9] = ']<span> ]</span></span>' .. "''"
output[4] = require('Module:Hatnote')._hatnote(table.concat(hatnote), {selfref=true}) or err("Error generating hatnote")
end
output[5] = '<' .. tag .. ' class="excerpt">\n'
if article ~= '{{{1}}}' then
local options = args -- turn template arguments into module options
options.paraflags = args.paragraphs
options.fileflags = args.files or 1
options.nobold = 1
options.fragment = args.fragment
options.keepTables = args.tables or 1
options.keepRefs = args.references or 1
options.keepSubsections = args.subsections
local pageNames = { (article .. '#' .. (section or '')) }
local text = main(pageNames, options)
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
output[6] = "[[Category:" .. d.brokenCategory .. "]]"
else
output[6] = frame:preprocess(text) or err("Error processing text")
end
else
output[6] = err("No article provided")
end
output[7] = '</' .. tag .. '>'
output[8] = is(args.indicator) and ('</' .. tag .. '>') or ''
output[9] = '</' .. tag .. '>'
output[10] = mw.title.getCurrentTitle().isContentPage and '[[Category:Articles with excerpts]]' or ''
return table.concat(output)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
function p.excerpt(frame) return excerpt(frame) end -- {{Excerpt}} transcludes part of an article into another article
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getSection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argImage(text) end
function p.checkimage(image) return checkImage(image) end
function p.parseimage(text, start) return parseImage(text, start) end
function p.cleanupText(text, options) return cleanupText(text, options) end
function p.main(pageNames, options) return main(pageNames, options) end
function p.numberflags(str) return numberFlags(str) end
return p
tsy316l99ffc2adax9n0zno1dixtsi1
797331
797330
2020-05-25T13:11:51Z
en>Sophivorus
0
Critical bugfix: regex was matching nested templates as well as the main template being evaluated
797331
Scribunto
text/plain
-- Get localized data
local d = require("Module:Excerpt/i18n")
local p = {}
-- Helper function to debug
-- Returns blank text or an error message if requested
local errors
local function err(msg,a,b)
local text = mw.ustring.format(d.error[msg] or msg or '',a,b)
if errors then error(text, 2) end
return ""
end
-- Helper function to test for truthy and falsy values
local function is(value)
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return false
end
return true
end
-- Helper function to match from a list regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
local function matchAny(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Help gsub to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function stripTemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchAny(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noRef = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noRef = mw.ustring.gsub(noRef, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noRef = mw.ustring.sub(noRef, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noRef, 3), "%b{}", stripTemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noRef = mw.ustring.gsub(noRef, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noRef = mw.ustring.gsub(noRef, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noRef ~= t then return noRef end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects, and processing file description pages for files.
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
local function getContent(page, frame)
local title = mw.title.new(page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Check image for suitability
local function checkImage(image)
local page = matchAny(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchAny(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
local fileDescription, fileTitle = getContent(page) -- get file description and title after following any redirect
if fileDescription and fileDescription ~= "" then -- found description on local wiki
if mw.ustring.match(fileDescription, "[Nn]on%-free") then return false end
fileDescription = mw.ustring.gsub(fileDescription, "%b{}", stripTemplate) -- remove DEFAULTSORT etc. to avoid side effects of frame:preprocess
elseif not fileTitle then
return false
else
-- try commons
fileDescription = "{{" .. fileTitle .. "}}"
end
frame = frame or mw.getCurrentFrame()
fileDescription = frame:preprocess(fileDescription)
return ( fileDescription and fileDescription ~= "" and not mw.ustring.match(fileDescription, "[Nn]on%-free") ) and true or false -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseImage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchAny(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parseCaption(caption)
if not caption then return nil end
local length = mw.ustring.len(caption)
local position = 1
while position <= length do
local linkStart, linkEnd = mw.ustring.find(caption, "%b[]", position)
linkStart = linkStart or length + 1 -- avoid comparison with nil when no link
local templateStart, templateEnd = mw.ustring.find(caption, "%b{}", position)
templateStart = templateStart or length + 1 -- avoid comparison with nil when no template
local argEnd = mw.ustring.find(caption, "[|}]", position) or length + 1
if linkStart < templateStart and linkStart < argEnd then
position = linkEnd + 1 -- skip wikilink
elseif templateStart < argEnd then
position = templateEnd + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argEnd - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argImage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", captureFrom)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgName = mw.ustring.lower(argname)
if mw.ustring.find(lcArgName, "caption")
or mw.ustring.find(lcArgName, "size")
or mw.ustring.find(lcArgName, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", captureFrom)
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", captureFrom)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, caption = matchAny(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", captureFrom)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parseCaption(caption)
end
end
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookFrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local length = mw.ustring.len(altText)
local afterText = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookFrom) or length+1,
mw.ustring.match(altText, "()|", lookFrom) or length+1)
altText = mw.ustring.sub(altText, 1, afterText-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseImage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchAny(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
-- Help gsub convert imagemaps into standard images
local function convertImageMap(imagemap)
local image = matchAny(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberFlags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
local imageArgGroups = {
{"thumb", "thumbnail", "frame", "framed", "frameless"},
{"right", "left", "center", "none"},
{"baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom"}
}
local function modifyImage(image, fileArgs)
if fileArgs then
for _, filearg in pairs(mw.text.split(fileArgs, "|")) do -- handle fileArgs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(imageArgGroups) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. `1,3-5`) or a table (e.g. `{1=true,2=false,3=true,4=true,5=true}`
-- options.fileargs : args for the [[File:]] syntax, such as `left`
-- @param filesOnly : If set, only return the files and not the prose
local function parse(text, options, filesOnly)
local allParagraphs = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberFlags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allParagraphs = false end -- if any para specifically requested, don't keep all
end
end
if filesOnly then
allParagraphs = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberFlags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileArgs = options.fileargs and mw.text.trim(options.fileargs)
if fileArgs == '' then fileArgs = nil end
local leadStart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local fileText = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileArgs)
if checkImage(f) then fileText = fileText .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadStart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchAny(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if leadStart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not filesOnly and not startLine then t = t .. token end
elseif matchAny(token, "^{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif is(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argImage(token) or {}
if not images then
local image = parseImage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkImage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
end
end
else -- the next token in text is not a template
token = parseImage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkImage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterEnd = mw.ustring.len(text) + 1
local blankPosition = mw.ustring.find(text, "\n%s*\n") or afterEnd -- position of next paragraph delimiter (or end of text)
local endPosition = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterEnd,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterEnd,
blankPosition)
token = mw.ustring.sub(text, 1, endPosition-1)
if blankPosition < afterEnd and blankPosition == endPosition then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankPosition)
end
local isHatnote = not(leadStart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadStart = leadStart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allParagraphs or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return fileText, text
end
local function cleanupText(text, options)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not is(options.keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
if not is(options.keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", stripTemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImageMap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getSection(text, section, mainOnly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return nil end -- no such section
local nextSection
if mainOnly then
nextSection = "\n==.*" -- Main part of section terminates at any level of header
else
nextSection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextSection, "") -- remove later sections with headings at this level or higher
return content
end
-- Remove unmatched <tag> or </tag> tags
local function fixTags(text, tag)
local startCount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startCount = startCount + 1 end
local endCount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endCount = endCount + 1 end
if startCount > endCount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endCount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endCount > startCount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endCount - startCount)
end
return text
end
-- Main function returns a string value: text of the lead of a page
local function main(pageNames, options)
if not pageNames or #pageNames < 1 then return err("No page names given") end
local pageName
local text
local pageCount = #pageNames
local firstPage = pageNames[1] or "(nil)" -- save for error message, as it the name will be deleted
local gotOptions
local pageOptionsString
local section
-- read the page, or a random one if multiple pages were provided
if pageCount > 1 then math.randomseed(os.time()) end
while not text and pageCount > 0 do
local pageNumber = 1
if pageCount > 1 then pageNumber = math.random(pageCount) end -- pick a random title
pageName = pageNames[pageNumber]
if pageName and pageName ~= "" then
-- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2...
local pn
pn, gotOptions, pageOptionsString = mw.ustring.match(pageName, "^%s*(%[%b[]%])%s*(|?)(.*)")
if pn then
pageName = mw.ustring.match(pn, "%[%[([^|%]]*)") -- turn [[page|text]] into page, discarding text
else -- we have page or page|opt...
pageName, gotOptions, pageOptionsString = mw.ustring.match(pageName, "%s*([^|]*[^|%s])%s*(|?)(.*)")
end
if pageName and pageName ~= "" then
local pn
pn, section = mw.ustring.match(pageName, "(.-)#(.*)")
pageName = pn or pageName
text, normalisedPageName = getContent(pageName)
if is(options.fragment) then
local frame = mw.getCurrentFrame()
text = frame:callParserFunction('#lst', normalisedPageName, options.fragment)
end
if not normalisedPageName then
return err("No title for page name " .. pageName)
else
pageName = normalisedPageName
end
if text and options.nostubs then
local isStub = mw.ustring.find(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}")
if isStub then text = nil end
end
if not section then
section = mw.ustring.match(pageName, ".-#(.*)") -- parse redirect to Page#Section
end
if text and section and section ~= "" then text = getSection(text, section) end
end
end
if not text then table.remove(pageNames, pageNumber) end -- this one didn't work; try another
pageCount = pageCount - 1 -- ensure that we exit the loop after at most #pageNames iterations
end
if not text then return err("Cannot read a valid page: first name is " .. firstPage) end
text = cleanupText(text, options)
local pageOptions = {} -- pageOptions (even if value is "") have priority over global options
for k, v in pairs(options) do pageOptions[k] = v end
if gotOptions and gotOptions ~= "" then
for _, t in pairs(mw.text.split(pageOptionsString, "|")) do
local k, v = mw.ustring.match(t, "%s*([^=]-)%s*=(.-)%s*$")
pageOptions[k] = v
end
pageOptions.paraflags = numberFlags(pageOptions["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
pageOptions.fileflags = numberFlags(pageOptions["files"] or "") -- parse file numbers
if pageOptions.more and pageOptions.more == "" then pageOptions.more = "Read more..." end -- more= is short for this default text
end
local fileText
fileText, text = parse(text, pageOptions)
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pageName) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pageName) .. "'''", 1, true) -- plain search: special characters in pageName represent themselves
if pos then
local len = mw.ustring.len(pageName)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if a < 100 and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return "'''[[" .. pageName .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pageName|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
-- remove '''bold text''' if requested
if is(pageOptions.nobold) then text = mw.ustring.gsub(text, "'''", "") end
text = fileText .. text
-- Seek and destroy unterminated templates and wikilinks
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
repeat -- do similar for [[wikilink]]s
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([{}%[%]])%1[^\27].*", "") -- remove unmatched {{, }}, [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([{}%[%]])%1$", "") -- remove unmatched {{, }}, [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, ]E]E → ]], etc.
-- Ensure div tags match
text = fixTags(text, "div")
if pageOptions.more then text = text .. " '''[[" .. pageName .. "|" .. pageOptions.more .. "]]'''" end -- wikilink to article for more info
if pageOptions.list and not pageOptions.showall then -- add a collapsed list of pages which might appear
local listtext = pageOptions.list
if listtext == "" then listtext = "Other articles" end
text = text .. "{{collapse top|title={{resize|85%|" ..listtext .. "}}|bg=fff}}{{hlist"
for _, p in pairs(pageNames) do
if mw.ustring.match(p, "%S") then text = text .. "|[[" .. mw.text.trim(p) .. "]]" end
end
text = text .. "}}\n{{collapse bottom}}"
end
return text
end
-- Shared template invocation code for lead and random functions
local function invoke(frame, template)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
errors = args["errors"] -- set the module level boolean used in local function err
local articleCount = #args -- must be 1 except with selected=Foo and Foo=Somepage
if articleCount < 1 and not (template == "selected" and args[template] and args[args[template]]) then
return err("No articles provided")
end
local pageNames = {}
if template == "lead" then
pageNames = { args[1] }
elseif template == "linked" or template == "listitem" then
-- Read named page and find its wikilinks
local page = args[1]
local text, title = getContent(page)
if not title then
return err("No title for page name " .. page)
elseif not text then
return err("No content for page name " .. page)
end
if args["section"] then -- check relevant section only
text = getSection(text, args["section"], args["sectiononly"])
if not text then return err("No section " .. args["section"] .. " in page " .. page) end
end
-- replace annotated links with real links
text = mw.ustring.gsub(text, "{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}", "[[%1]]")
if template == "linked" then
for p in mw.ustring.gmatch(text, "%[%[%s*([^%]|\n]*)") do table.insert(pageNames, p) end
else -- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section
text = mw.ustring.gsub(text, "\n== *See also.*", "")
for p in mw.ustring.gmatch(text, "\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)") do table.insert(pageNames, p) end
end
elseif template == "random" then
-- accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pageNames, p) end
end
elseif template == "selected" then
local articleKey = args[template]
if tonumber(articleKey) then -- normalise article number into the range 1..#args
articleKey = articleKey % articleCount
if articleKey == 0 then articleKey = articleCount end
end
pageNames = { args[articleKey] }
end
local options = args -- pick up miscellaneous options: more, errors, fileargs
options.paraflags = numberFlags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberFlags(args["files"] or "") -- parse file numbers
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
local text = ""
if options.showall then
local separator = ""
for _, p in pairs(pageNames) do
local t = main({ p }, options)
if t ~= "" then
text = text .. separator .. t
separator = options.showall
if separator == "" then separator = "{{clear}}{{hr}}" end
end
end
else
text = main(pageNames, options)
end
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
return "[[Category:" .. d.brokenCategory .. "]]"
else
return frame:preprocess(text)
end
end
-- Replicate {{Excerpt}} entirely in Lua for reduced Post-expand include size
local function excerpt(frame)
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
for k, v in pairs(frame:getParent().args) do args[k] = v end
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
local tag = is(args.tag) and args.tag or 'div'
local article = is(args.article) and args.article or args[1] or '{{{1}}}'
local section = is(args.section) and args.section or args[2]
local output = {}
output[1] = frame:extensionTag{ name = 'templatestyles', args = {src='Excerpt/styles.css'} }
output[2] = '<' .. tag .. ' class="excerpt-block">'
output[3] = is(args.indicator) and ('<' .. tag .. ' class="excerpt-indicator">') or ''
if is(args.nohat) then
output[4] = ''
else
local hatnote = {}
hatnote[1] = 'This' .. (is(args.indicator) and '' or ' section') .. ' is an excerpt from '
hatnote[2] = '[['
hatnote[3] = article .. (is(section) and ('#' .. frame:callParserFunction( 'urlencode', section, 'WIKI' )) or '')
hatnote[4] = '|'
hatnote[5] = article .. (is(section) and (frame:callParserFunction( '#tag:nowiki', ' § ' ) .. section) or '')
hatnote[6] = ']]'
hatnote[7] = "''" .. '<span class="mw-editsection-like plainlinks"><span>[ </span>['
local title = mw.title.new(article) or mw.title.getCurrentTitle()
hatnote[8] = title:fullUrl('action=edit') .. ' edit'
hatnote[9] = ']<span> ]</span></span>' .. "''"
output[4] = require('Module:Hatnote')._hatnote(table.concat(hatnote), {selfref=true}) or err("Error generating hatnote")
end
output[5] = '<' .. tag .. ' class="excerpt">\n'
if article ~= '{{{1}}}' then
local options = args -- turn template arguments into module options
options.paraflags = args.paragraphs
options.fileflags = args.files or 1
options.nobold = 1
options.fragment = args.fragment
options.keepTables = args.tables or 1
options.keepRefs = args.references or 1
options.keepSubsections = args.subsections
local pageNames = { (article .. '#' .. (section or '')) }
local text = main(pageNames, options)
if text == "" and d.brokenCategory and d.brokenCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
output[6] = "[[Category:" .. d.brokenCategory .. "]]"
else
output[6] = frame:preprocess(text) or err("Error processing text")
end
else
output[6] = err("No article provided")
end
output[7] = '</' .. tag .. '>'
output[8] = is(args.indicator) and ('</' .. tag .. '>') or ''
output[9] = '</' .. tag .. '>'
output[10] = mw.title.getCurrentTitle().isContentPage and '[[Category:Articles with excerpts]]' or ''
return table.concat(output)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return invoke(frame, "lead") end -- {{Transclude lead excerpt}} reads the first and only article
function p.linked(frame) return invoke(frame, "linked") end -- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page
function p.listitem(frame) return invoke(frame, "listitem") end -- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page
function p.random(frame) return invoke(frame, "random") end -- {{Transclude random excerpt}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter
function p.excerpt(frame) return excerpt(frame) end -- {{Excerpt}} transcludes part of an article into another article
-- Entry points for other Lua modules
function p.getContent(page, frame) return getContent(page, frame) end
function p.getsection(text, section) return getSection(text, section) end
function p.parse(text, options, filesOnly) return parse(text, options, filesOnly) end
function p.argimage(text) return argImage(text) end
function p.checkimage(image) return checkImage(image) end
function p.parseimage(text, start) return parseImage(text, start) end
function p.cleanupText(text, options) return cleanupText(text, options) end
function p.main(pageNames, options) return main(pageNames, options) end
function p.numberflags(str) return numberFlags(str) end
return p
sievx9lgslmbhs1cy4j61zsp5we2s7m
797332
797331
2020-06-01T12:56:35Z
en>Sophivorus
0
Update to new version
797332
Scribunto
text/plain
-- Get localized data
local d = require("Module:Excerpt/i18n")
local p = {}
-- Helper function to test for truthy and falsy values
local function is(value)
if not value or value == "" or value == "0" or value == "false" or value == "no" then
return false
end
return true
end
-- Error handling function
-- Throws a Lua error or returns an empty string if error reporting is disabled
errors = true -- show errors by default
local function luaError(message, value)
if not is(errors) then return "" end -- error reporting is disabled
message = d.errors[message] or message or ""
message = mw.ustring.format(message, value)
error(message, 2)
end
-- Error handling function
-- Returns a wiki friendly error or an empty string if error reporting is disabled
local function wikiError(message, value)
if not is(errors) then return "" end -- error reporting is disabled
message = d.errors[message] or message or ""
message = mw.ustring.format(message, value)
message = d.errors.prefix .. message
if mw.title.getCurrentTitle().isContentPage then
local errorsCategory = mw.title.new(d.errorsCategory, 'Category')
if errorsCategory then message = message .. '[[' .. errorsCategory.prefixedText .. ']]' end
end
message = mw.html.create('div'):addClass('error'):wikitext(message)
return message
end
-- Helper function to match from a list regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
local function matchAny(text, pre, list, post, init)
local match = {}
for i = 1, #list do
match = { mw.ustring.match(text, pre .. list[i] .. post, init) }
if match[1] then return unpack(match) end
end
return nil
end
-- Helper function to convert imagemaps into standard images
local function convertImageMap(imagemap)
local image = matchAny(imagemap, "[>\n]%s*", d.fileNamespaces, "[^\n]*")
if image then
return "<!--imagemap-->[[" .. mw.ustring.gsub(image, "[>\n]%s*", "", 1) .. "]]"
else
return "" -- remove entire block if image can't be extracted
end
end
-- Helper function to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- For example: "1,3-5" to {1=true,2=false,3=true,4=true,5=true}
local function numberFlags(str)
if not str then return {} end
local flags = {}
local ranges = mw.text.split(str, ",") -- parse ranges: "1,3-5" to {"1","3-5"}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" to min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" to min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Helper function to convert template arguments into an array of arguments fit for get()
local function parseArgs(frame)
local args = {}
for key, value in pairs(frame:getParent().args) do args[key] = value end
for key, value in pairs(frame.args) do args[key] = value end -- args from a Lua call have priority over parent args from template
args.paraflags = numberFlags(args["paragraphs"] or "") -- parse paragraphs: "1,3-5" to {"1","3-5"}
args.fileflags = numberFlags(args["files"] or "") -- parse file numbers
return args
end
-- Helper function to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT
local function stripTemplate(t)
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
if matchAny(t, "^{{%s*", d.unwantedInlineTemplates, "%s*%f[|}]") then return "" end
-- If template is wanted but produces an unwanted reference then return the string with |shortref or |ref removed
local noRef = mw.ustring.gsub(t, "|%s*shortref%s*%f[|}]", "")
noRef = mw.ustring.gsub(noRef, "|%s*ref%s*%f[|}]", "")
-- If a wanted template has unwanted nested templates, purge them too
noRef = mw.ustring.sub(noRef, 1, 2) .. mw.ustring.gsub(mw.ustring.sub(noRef, 3), "%b{}", stripTemplate)
-- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar
noRef = mw.ustring.gsub(noRef, "^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*", "%1")
-- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English
noRef = mw.ustring.gsub(noRef, "^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*", "%1")
if noRef ~= t then return noRef end
return nil -- not an unwanted template: keep
end
-- Get a page's content, following redirects
-- Also returns the page name, or the target page name if a redirect was followed, or false if no page found
-- For file pages, returns the content of the file description page
local function getContent(page)
local title = mw.title.new(page)
if not title then return false, false end
local target = title.redirectTarget
if target then title = target end
return title:getContent(), title.prefixedText
end
-- Get the tables only
local function getTables(text, options)
local tables = {}
for candidate in mw.ustring.gmatch(text, "%b{}") do
if mw.ustring.sub(candidate, 1, 2) == '{|' then
table.insert(tables, candidate)
end
end
return table.concat(tables, '\n')
end
-- Get the lists only
local function getLists(text, options)
local lists = {}
for list in mw.ustring.gmatch(text, "\n[*#][^\n]+") do
table.insert(lists, list)
end
return table.concat(lists, '\n')
end
-- Check image for suitability
local function checkImage(image)
local page = matchAny(image, "", d.fileNamespaces, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg, audio, etc.)
local fileTypes = {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}
if not matchAny(page, "%.", fileTypes, "%s*$") then return false end
-- Check the local wiki
local fileDescription, fileTitle = getContent(page) -- get file description and title after following any redirect
if not fileTitle or fileTitle == "" then return false end -- the image doesn't exist
-- Check Commons
if not fileDescription or fileDescription == "" then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess("{{" .. fileTitle .. "}}")
end
-- Filter non-free images
if not fileDescription or fileDescription == "" or mw.ustring.match(fileDescription, "[Nn]on%-free") then return false end
return true
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local function parseImage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = matchAny(text, startre .. "%[%[%s*", d.fileNamespaces, "%s*:.*") -- [[File: or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return image
end
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parseCaption(caption)
if not caption then return nil end
local length = mw.ustring.len(caption)
local position = 1
while position <= length do
local linkStart, linkEnd = mw.ustring.find(caption, "%b[]", position)
linkStart = linkStart or length + 1 -- avoid comparison with nil when no link
local templateStart, templateEnd = mw.ustring.find(caption, "%b{}", position)
templateStart = templateStart or length + 1 -- avoid comparison with nil when no template
local argEnd = mw.ustring.find(caption, "[|}]", position) or length + 1
if linkStart < templateStart and linkStart < argEnd then
position = linkEnd + 1 -- skip wikilink
elseif templateStart < argEnd then
position = templateEnd + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argEnd - 1)
end
end
return caption -- No terminator found: return entire caption
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argImage(text)
local token = nil
local hasNamedArgs = mw.ustring.find(text, "|") and mw.ustring.find(text, "=")
if not hasNamedArgs then return nil end -- filter out any template that obviously doesn't contain an image
-- ensure image map is captured
text = mw.ustring.gsub(text, '<!%-%-imagemap%-%->', '|imagemap=')
-- find all images
local hasImages = false
local images = {}
local captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local argname, position, image = mw.ustring.match(text, "|%s*([^=|]-[Ii][Mm][Aa][Gg][Ee][^=|]-)%s*=%s*()(.*)", captureFrom)
if image then -- ImageCaption=, image_size=, image_upright=, etc. do not introduce an image
local lcArgName = mw.ustring.lower(argname)
if mw.ustring.find(lcArgName, "caption")
or mw.ustring.find(lcArgName, "size")
or mw.ustring.find(lcArgName, "upright") then
image = nil
end
end
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|]-[Pp][Hh][Oo][Tt][Oo][^=|]-%s*=%s*()(.*)", captureFrom)
if image then
hasImages = true
images[position] = image
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, image = mw.ustring.match(text, "|%s*[^=|{}]-%s*=%s*()%[?%[?([^|{}]*%.%a%a%a%a?)%s*%f[|}]", captureFrom)
if image then
hasImages = true
if not images[position] then
images[position] = image
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
if not hasImages then return nil end
-- find all captions
local captions = {}
captureFrom = 1
while captureFrom < mw.ustring.len(text) do
local position, caption = matchAny(text, "|%s*", d.captionParams, "%s*=%s*()([^\n]+)", captureFrom)
if caption then
-- extend caption to parse "| caption = Foo {{Template\n on\n multiple lines}} Bar\n"
local bracedCaption = mw.ustring.match(text, "^[^\n]-%b{}[^\n]+", position)
if bracedCaption and bracedCaption ~= "" then caption = bracedCaption end
caption = mw.text.trim(caption)
local captionStart = mw.ustring.sub(caption, 1, 1)
if captionStart == '|' or captionStart == '}' then caption = nil end
end
if caption then
-- find nearest image, and use same index for captions table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not captions[i] then
captions[i] = parseCaption(caption)
end
end
end
captureFrom = position
else
captureFrom = mw.ustring.len(text)
end
end
-- find all alt text
local altTexts = {}
for position, altText in mw.ustring.gmatch(text, "|%s*[Aa][Ll][Tt]%s*=%s*()([^\n]*)") do
if altText then
-- altText is terminated by }} or |, but first skip any matched [[...]] and {{...}}
local lookFrom = math.max( -- find position after whichever comes last: start of string, end of last ]] or end of last }}
mw.ustring.match(altText, ".*{%b{}}()") or 1, -- if multiple {{...}}, .* consumes all but one, leaving the last for %b
mw.ustring.match(altText, ".*%[%b[]%]()") or 1)
local length = mw.ustring.len(altText)
local afterText = math.min( -- find position after whichever comes first: end of string, }} or |
mw.ustring.match(altText, "()}}", lookFrom) or length+1,
mw.ustring.match(altText, "()|", lookFrom) or length+1)
altText = mw.ustring.sub(altText, 1, afterText-1) -- chop off |... or }}... which is not part of [[...]] or {{...}}
altText = mw.text.trim(altText)
local altTextStart = mw.ustring.sub(altText, 1, 1)
if altTextStart == '|' or altTextStart == '}' then altText = nil end
end
if altText then
-- find nearest image, and use same index for altTexts table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not altTexts[i] then
altTexts[i] = altText
end
end
end
end
end
-- find all image sizes
local imageSizes = {}
for position, imageSizeMatch in mw.ustring.gmatch(text, "|%s*[Ii][Mm][Aa][Gg][Ee][ _]?[Ss][Ii][Zz][Ee]%s*=%s*()([^}|\n]*)") do
local imageSize = mw.ustring.match(imageSizeMatch, "=%s*([^}|\n]*)")
if imageSize then
imageSize = mw.text.trim(imageSize )
local imageSizeStart = mw.ustring.sub(imageSize, 1, 1)
if imageSizeStart == '|' or imageSizeStart == '}' then imageSize = nil end
end
if imageSize then
-- find nearest image, and use same index for imageSizes table
local i = position
while i > 0 and not images[i] do
i = i - 1
if images[i] then
if not imageSizes[i] then
imageSizes[i] = imageSize
end
end
end
end
end
-- sort the keys of the images table (in a table sequence), so that images can be iterated over in order
local keys = {}
for key, val in pairs(images) do
table.insert(keys, key)
end
table.sort(keys)
-- add in relevant optional parameters for each image: caption, alt text and image size
local imageTokens = {}
for _, index in ipairs(keys) do
local image = images[index]
local token = parseImage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|\n]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchAny(image, "^", d.fileNamespaces, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = captions[index]
if caption and mw.ustring.match(caption, "%S") then token = token .. "|" .. caption end
local alt = altTexts[index]
if alt then token = token .. "|alt=" .. alt end
local image_size = imageSizes[index]
if image_size and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
table.insert(imageTokens, token)
end
return imageTokens
end
local function modifyImage(image, fileArgs)
if fileArgs then
for _, filearg in pairs(mw.text.split(fileArgs, "|")) do -- handle fileArgs=left|border etc.
local fa = mw.ustring.gsub(filearg, "=.*", "") -- "upright=0.75" → "upright"
local group = {fa} -- group of "border" is ["border"]...
for _, g in pairs(d.imageParams) do
for _, a in pairs(g) do
if fa == a then group = g end -- ...but group of "left" is ["right", "left", "center", "none"]
end
end
for _, a in pairs(group) do
image = mw.ustring.gsub(image, "|%s*" .. a .. "%f[%A]%s*=[^|%]]*", "") -- remove "|upright=0.75" etc.
image = mw.ustring.gsub(image, "|%s*" .. a .. "%s*([|%]])", "%1") -- replace "|left|" by "|" etc.
end
image = mw.ustring.gsub(image, "([|%]])", "|" .. filearg .. "%1", 1) -- replace "|" by "|left|" etc.
end
end
return image
end
-- a basic parser to trim down extracted wikitext
-- @param text : Wikitext to be processed
-- @param options : A table of options...
-- options.paraflags : Which number paragraphs to keep, as either a string (e.g. '1,3-5') or a table (e.g. {1=true,2=false,3=true,4=true,5=true}. If not present, all paragraphs will be kept.
-- options.fileflags : table of which files to keep, as either a string (e.g. '1,3-5') or a table (e.g. {1=true,2=false,3=true,4=true,5=true}
-- options.fileargs : args for the [[File:]] syntax, such as 'left'
-- options.filesOnly : only return the files and not the prose
local function parse(text, options)
local allParagraphs = true -- keep all paragraphs?
if options.paraflags then
if type(options.paraflags) ~= "table" then options.paraflags = numberFlags(options.paraflags) end
for _, v in pairs(options.paraflags) do
if v then allParagraphs = false end -- if any para specifically requested, don't keep all
end
end
if is(options.filesOnly) then
allParagraphs = false
options.paraflags = {}
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
if type(options.fileflags) ~= "table" then options.fileflags = numberFlags(options.fileflags) end
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
end
end
local fileArgs = options.fileargs and mw.text.trim(options.fileargs)
if fileArgs == '' then fileArgs = nil end
local leadStart = nil -- have we found some text yet?
local t = "" -- the stripped down output text
local fileText = "" -- output text with concatenated [[File:Foo|...]]\n entries
local files = 0 -- how many images so far
local paras = 0 -- how many paragraphs so far
local startLine = true -- at the start of a line (no non-spaces found since last \n)?
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
-- Add named files
local f = options.files
if f and mw.ustring.match(f, "[^%d%s%-,]") then -- filename rather than number list
f = mw.ustring.gsub(f, "^%s*File%s*:%s*", "", 1)
f = mw.ustring.gsub(f, "^%s*Image%s*:%s*", "", 1)
f = "[[File:" .. f .. "]]"
f = modifyImage(f, "thumb")
f = modifyImage(f, fileArgs)
if checkImage(f) then fileText = fileText .. f .. "\n" end
end
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}} or {| Table |}
if not leadStart and not token then token = mw.ustring.match(text, "^%b<>%s*%b{}%s*") end -- allow <tag>{{template}} before lead has started
local line = mw.ustring.match(text, "[^\n]*")
if token and line and mw.ustring.len(token) < mw.ustring.len(line) then -- template is followed by text (but it may just be other templates)
line = mw.ustring.gsub(line, "%b{}", "") -- remove all templates from this line
line = mw.ustring.gsub(line, "%b<>", "") -- remove all HTML tags from this line
-- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line
if mw.ustring.find(line, "%S") and not matchAny(line, "^%s*", { "{{", "%[%[%s*[Ff]ile:", "%[%[%s*[Ii]mage:" }, "") then
token = nil
end
end
if token then -- found a template which is not the prefix to a line of text
if is(options.keepTables) and mw.ustring.sub(token, 1, 2) == '{|' then
t = t .. token -- keep tables
elseif mw.ustring.sub(token, 1, 3) == '{{#' then
t = t .. token -- keep parser functions
elseif leadStart then -- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.)
if not is(options.filesOnly) and not startLine then t = t .. token end
elseif matchAny(token, "^{{%s*", d.wantedBlockTemplates, "%s*%f[|}]") then
t = t .. token -- keep wanted block templates
elseif files < maxfile then -- discard template, but if we are still collecting images...
local images = argImage(token) or {}
if not images then
local image = parseImage(token, false) -- look for embedded [[File:...]], |image=, etc.
if image then table.insert(images, image) end
end
for _, image in ipairs(images) do
if files < maxfile and checkImage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
files = files + 1 -- count the file, whether displaying it or not
if options.fileflags and options.fileflags[files] then -- if displaying this image
image = modifyImage(image, "thumb")
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
end
end
else -- the next token in text is not a template
token = parseImage(text, true)
if token then -- the next token in text looks like an image
if files < maxfile and checkImage(token) then -- if more images are wanted and this is a wanted image
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
image = modifyImage(image, fileArgs)
fileText = fileText .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterEnd = mw.ustring.len(text) + 1
local blankPosition = mw.ustring.find(text, "\n%s*\n") or afterEnd -- position of next paragraph delimiter (or end of text)
local endPosition = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterEnd,
mw.ustring.find(text, "%[%[%s*[Ii]mage%s*:") or afterEnd,
blankPosition)
token = mw.ustring.sub(text, 1, endPosition-1)
if blankPosition < afterEnd and blankPosition == endPosition then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankPosition)
end
local isHatnote = not(leadStart) and mw.ustring.sub(token, 1, 1) == ':'
if not isHatnote then
leadStart = leadStart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allParagraphs or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end
end -- of "else got a paragraph"
end -- of "else not a template"
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
startLine = mw.ustring.find(token, "\n%s*$") -- will the next token be the first non-space on a line?
until not text or text == "" or not token or token == "" -- loop until all text parsed
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
return fileText .. text
end
local function cleanupText(text, options)
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove noinclude bits
if mw.ustring.find(text, "[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]") then -- avoid expensive search if possible
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text between onlyinclude sections
text = mw.ustring.gsub(text, "^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>", "") -- remove text before first onlyinclude section
text = mw.ustring.gsub(text, "</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*", "") -- remove text after last onlyinclude section
end
if not is(options.keepSubsections) then
text = mw.ustring.gsub(text, "\n==.*","") -- remove first ==Heading== and everything after it
text = mw.ustring.gsub(text, "^==.*","") -- ...even if the lead is empty
end
if not is(options.keepRefs) then
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "%b{}", stripTemplate) -- remove unwanted templates such as references
end
text = mw.ustring.gsub(text, "<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>", "") -- remove musical scores
text = mw.ustring.gsub(text, "<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>", convertImageMap) -- convert imagemaps into standard images
text = mw.ustring.gsub(text, "%s*{{%s*[Tt][Oo][Cc].-}}", "") -- remove most common tables of contents
text = mw.ustring.gsub(text, "%s*__[A-Z]*TOC__", "") -- remove TOC behavior switches
text = mw.ustring.gsub(text, "\n%s*{{%s*[Pp]p%-.-}}", "\n") -- remove protection templates
text = mw.ustring.gsub(text, "%s*{{[^{|}]*[Ss]idebar%s*}}", "") -- remove most sidebars
text = mw.ustring.gsub(text, "%s*{{[^{|}]*%-[Ss]tub%s*}}", "") -- remove most stub templates
text = mw.ustring.gsub(text, "%s*%[%[%s*:?[Cc]ategory:.-%]%]", "") -- remove categories
text = mw.ustring.gsub(text, "^:[^\n]+\n","") -- remove DIY hatnote indented with a colon
return text
end
-- Parse a ==Section== from a page
local function getSection(text, section, mainOnly)
local escapedSection = mw.ustring.gsub(mw.uri.decode(section), "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- %26 → & etc, then ^ → %^ etc.
local level, content = mw.ustring.match(text .. "\n", "\n(==+)%s*" .. escapedSection .. "%s*==.-\n(.*)")
if not content then return luaError("sectionNotFound", section) end
local nextSection
if mainOnly then
nextSection = "\n==.*" -- Main part of section terminates at any level of header
else
nextSection = "\n==" .. mw.ustring.rep("=?", #level - 2) .. "[^=].*" -- "===" → "\n===?[^=].*", matching "==" or "===" but not "===="
end
content = mw.ustring.gsub(content, nextSection, "") -- remove later sections with headings at this level or higher
if mw.ustring.match(content, "^%s*$") then return luaError("sectionEmpty", section) end
return content
end
-- Parse a <section begin="Name of the fragment">
-- @todo Implement custom parsing of fragments rather than relying on #lst
local function getFragment(page, fragment)
local frame = mw.getCurrentFrame()
local text = frame:callParserFunction('#lst', page, fragment)
if mw.ustring.match(text, "^%s*$") then return luaError("fragmentEmpty", fragment) end
return text
end
-- Remove unmatched <tag> or </tag> tags
local function fixTags(text, tag)
local startCount = 0
for i in mw.ustring.gmatch(text, "<%s*" .. tag .. "%f[^%w_].->") do startCount = startCount + 1 end
local endCount = 0
for i in mw.ustring.gmatch(text, "<%s*/" .. tag .. "%f[^%w_].->") do endCount = endCount + 1 end
if startCount > endCount then -- more <tag> than </tag>: remove the last few <tag>s
local i = 0
text = mw.ustring.gsub(text, "<%s*" .. tag .. "%f[^%w_].->", function(t)
i = i + 1
if i > endCount then return "" else return nil end
end) -- "end" here terminates the anonymous replacement function(t) passed to gsub
elseif endCount > startCount then -- more </tag> than <tag>: remove the first few </tag>s
text = mw.ustring.gsub(text, "<%s*/" .. tag .. "%f[^%w_].->", "", endCount - startCount)
end
return text
end
local function fixTemplates(text)
repeat -- hide matched {{template}}s including nested templates
local t = text
text = mw.ustring.gsub(text, "{(%b{})}", "\27{\27%1\27}\27") -- {{sometemplate}} → E{Esometemplate}E}E where E represents escape
text = mw.ustring.gsub(text, "(< *math[^>]*>[^<]-)}}(.-< */math *>)", "%1}\27}\27%2") -- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math>
until text == t
text = text.gsub(text, "([{}])%1[^\27].*", "") -- remove unmatched {{, }} and everything thereafter, avoiding }E}E etc.
text = text.gsub(text, "([{}])%1$", "") -- remove unmatched {{, }} at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: E{E{ → {{, etc.
return text
end
local function fixLinks(text)
repeat -- hide matched [[wikilink]]s including nested links like [[File:Example.jpg|Some [[nested]] link.]]
local t = text
text = mw.ustring.gsub(text, "%[(%b[])%]", "\27[\27%1\27]\27")
until text == t
text = text.gsub(text, "([%[%]])%1[^\27].*", "") -- remove unmatched [[ or ]] and everything thereafter, avoiding ]E]E etc.
text = text.gsub(text, "([%[%]])%1$", "") -- remove unmatched [[ or ]] at end of text
text = mw.ustring.gsub(text, "\27", "") -- unhide matched pairs: ]E]E → ]], etc.
return text
end
-- Replace the first call to each reference defined outside of the text for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
local function fixRefs(text, page, full)
if not full then full = getContent(page) end
local refNames = {}
local refName
local refBody
local position = 1
while position < mw.ustring.len(text) do
refName, position = mw.ustring.match(text, "<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?([^\"'>]+)[\"']?[^>]*/%s*>()", position)
if refName then
refName = mw.text.trim(refName)
if not refNames[refName] then -- make sure we process each ref name only once
table.insert(refNames, refName)
refName = mw.ustring.gsub(refName, "[%^%$%(%)%.%[%]%*%+%-%?%%]", "%%%0") -- escape special characters
refBody = mw.ustring.match(text, "<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?%s*" .. refName .. "%s*[\"']?[^>/]*>.-<%s*/%s*[Rr][Ee][Ff]%s*>")
if not refBody then -- the ref body is not in the excerpt
refBody = mw.ustring.match(full, "<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?%s*" .. refName .. "%s*[\"']?[^/>]*>.-<%s*/%s*[Rr][Ee][Ff]%s*>")
if refBody then -- the ref body was found elsewhere
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?%s*" .. refName .. "%s*[\"']?[^>]*/?%s*>", refBody, 1)
end
end
end
else
position = mw.ustring.len(text)
end
end
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?([^\"'>/]+)[\"']?[^>/]*(/?)%s*>", '<ref name="' .. page .. ' %1" %2>')
text = mw.ustring.gsub(text, "<%s*[Rr][Ee][Ff][^>]*group%s*=%s*[\"']?[^\"'>/]+[\"']%s*>", '<ref>')
return text
end
-- Replace the bold title or synonym near the start of the article by a wikilink to the article
function linkBold(text, page)
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find(text, "'''" .. lang:ucfirst(page) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(page) .. "'''", 1, true) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len(page)
text = mw.ustring.sub(text, 1, position + 2) .. "[[" .. mw.ustring.sub(text, position + 3, position + length + 2) .. "]]" .. mw.ustring.sub(text, position + length + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "()'''(.-'*)'''", function(a, b)
if not mw.ustring.find(b, "%[") then -- if not wikilinked
return "'''[[" .. page .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
return text
end
-- Main function for modules
local function get(page, options)
if options.errors then errors = options.errors end
if not page or page == "" then return luaError("noPage") end
local text
page, section = mw.ustring.match(page, "([^#]+)#?([^#]*)")
text, page = getContent(page)
if not page then return luaError("noPage") end
if not text then return luaError("pageNotFound", page) end
local full = text -- save the full text for later
if is(options.fragment) then
text = getFragment(page, options.fragment)
end
if is(section) then
text = getSection(text, section)
end
-- Strip text of all undersirables
text = cleanupText(text, options)
text = parse(text, options)
-- Replace the bold title or synonym near the start of the article by a wikilink to the article
text = linkBold(text, page)
-- Remove '''bold text''' if requested
if is(options.nobold) then text = mw.ustring.gsub(text, "'''", "") end
-- Keep only tables if requested
if is(options.tablesOnly) then text = getTables(text) end
-- Keep only lists if requested
if is(options.listsOnly) then text = getLists(text) end
-- Seek and destroy unterminated templates, links and tags
text = fixTemplates(text)
text = fixLinks(text)
text = fixTags(text, "div")
-- Fix broken references
if is(options.keepRefs) then text = fixRefs(text, page, full) end
return text
end
-- Main invocation function for templates
local function main(frame)
local args = parseArgs(frame)
local page = args[1]
local ok, text = pcall(get, page, args)
if not ok then
text = d.errors.prefix .. text
if d.errorsCategory and d.errorsCategory ~= "" and mw.title.getCurrentTitle().isContentPage then
text = text .. '[[' .. d.errorsCategory .. ']]'
end
return mw.html.create('div'):addClass('error'):wikitext(text)
end
return frame:preprocess(text)
end
-- Entry points for templates
function p.main(frame) return main(frame) end
function p.wikiError(message, value) return wikiError(message, value) end
-- Entry points for other Lua modules
function p.get(page, options) return get(page, options) end
function p.getContent(page) return getContent(page) end
function p.getSection(text, section) return getSection(text, section) end
function p.getTables(text, options) return getTables(text, options) end
function p.getLists(text, options) return getLists(text, options) end
function p.parse(text, options) return parse(text, options) end
function p.parseImage(text, start) return parseImage(text, start) end
function p.parseArgs(frame) return parseArgs(frame) end
function p.argImage(text) return argImage(text) end
function p.checkImage(image) return checkImage(image) end
function p.cleanupText(text, options) return cleanupText(text, options) end
function p.luaError(message, value) return luaError(message, value) end
function p.is(value) return is(value) end
function p.numberFlags(str) return numberFlags(str) end
-- Entry points for backwards compatibility
function p.getsection(text, section) return getSection(text, section) end
function p.parseimage(text, start) return parseImage(text, start) end
function p.checkimage(image) return checkImage(image) end
function p.argimage(text) return argImage(text) end
function p.numberflags(str) return numberFlags(str) end
return p
qdws7a25ovwxcovcd3pkxzadtghpmek
797333
797332
2020-08-29T15:06:29Z
en>Sophivorus
0
Update to latest, see [[Module talk:Excerpt#New version]]
797333
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match(getArg(1), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files')
local lists = getArg('lists')
local tables = getArg('tables')
local sections = not yesno( getArg('sections') )
local templates = table.concat((config.templates or {}), ',')
local paragraphs = getArg('paragraphs')
local references = getArg('references')
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']]' -- remove nested links
else
hat = hat .. '[[' .. page .. ']]'
end
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
local ok, Hatnote = pcall(require, 'Module:Hatnote')
if ok then
hat = Hatnote._hatnote( hat, { extraclasses = 'dablink excerpt-hat', selfref = true } )
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates ~= '' and '-' .. templates,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if (files ~= '0' or not files) and not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and config.captions then
local templates = Transcluder.get(title, { only = 'templates', templates = templates, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
ji6uvuc38hwfqcjiqnzxs5613vomq7g
797334
797333
2020-10-07T14:51:24Z
en>Sophivorus
0
797334
Scribunto
text/plain
local Transcluder = require('Module:Transcluder/sandbox')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match(getArg(1), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files')
local lists = getArg('lists')
local tables = getArg('tables')
local sections = not yesno( getArg('sections') )
local templates = table.concat((config.templates or {}), ',')
local paragraphs = getArg('paragraphs')
local references = getArg('references')
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']]' -- remove nested links
else
hat = hat .. '[[' .. page .. ']]'
end
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
local ok, Hatnote = pcall(require, 'Module:Hatnote')
if ok then
hat = Hatnote._hatnote( hat, { extraclasses = 'dablink excerpt-hat', selfref = true } )
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates ~= '' and '-' .. templates,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if (files ~= '0' or not files) and not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and config.captions then
local templates = Transcluder.get(title, { only = 'templates', templates = templates, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
rtxc1u1r284m54k63p5x1gosu3np9az
797335
797334
2020-10-07T14:52:23Z
en>Sophivorus
0
My bad
797335
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match(getArg(1), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files')
local lists = getArg('lists')
local tables = getArg('tables')
local sections = not yesno( getArg('sections') )
local templates = table.concat((config.templates or {}), ',')
local paragraphs = getArg('paragraphs')
local references = getArg('references')
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']]' -- remove nested links
else
hat = hat .. '[[' .. page .. ']]'
end
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
local ok, Hatnote = pcall(require, 'Module:Hatnote')
if ok then
hat = Hatnote._hatnote( hat, { extraclasses = 'dablink excerpt-hat', selfref = true } )
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates ~= '' and '-' .. templates,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if (files ~= '0' or not files) and not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and config.captions then
local templates = Transcluder.get(title, { only = 'templates', templates = templates, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
ji6uvuc38hwfqcjiqnzxs5613vomq7g
797336
797335
2020-10-08T14:15:20Z
en>Sophivorus
0
Update to latest
797336
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match(getArg(1), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local sections = not yesno( getArg('sections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
local blacklist = table.concat((config.templates or {}), ',')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']]' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']]'
end
if edit then
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
end
hat = mw.html.create('div'):addClass('excerpt-hat'):wikitext(hat)
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates or '-' .. blacklist,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if not only and (files ~= '0' or not files) and not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and config.captions then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
2dbiiu8kj4fias2e4d3fpzhtxk1p2td
797337
797336
2020-10-08T14:34:48Z
en>Sophivorus
0
Add dablink
797337
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match(getArg(1), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local sections = not yesno( getArg('sections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
local blacklist = table.concat((config.templates or {}), ',')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']]' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']]'
end
if edit then
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
end
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates or '-' .. blacklist,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if not only and (files ~= '0' or not files) and not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and config.captions then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
fy6651xc7lretv76mrljnkk5g7rb6he
797338
797337
2020-11-17T13:52:59Z
en>Sophivorus
0
Update to latest
797338
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match(getArg(1), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local sections = not yesno( getArg('sections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
local blacklist = table.concat((config.templates or {}), ',')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']]' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']]'
end
if edit then
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
end
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates or '-' .. blacklist,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
crts6ogki8oeif8xtmatr9f45u0qncc
797339
797338
2021-02-20T02:45:14Z
en>Matt Fitzpatrick
0
implemented edit request by Snaevar, fixes article parameter, which previously did nothing
797339
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1) or getArg('article')
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match(page, '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local sections = not yesno( getArg('sections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
local blacklist = table.concat((config.templates or {}), ',')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']]' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']]'
end
if edit then
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
end
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates or '-' .. blacklist,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
gq92jlur3vhul28zvgmukbf57ped2ll
797340
797339
2021-02-22T21:40:47Z
en>Sophivorus
0
Fix the previous fix
797340
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1, getArg('article') )
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match( getArg(1, getArg('article') ), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local sections = not yesno( getArg('sections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
local blacklist = table.concat((config.templates or {}), ',')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']]' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']]'
end
if edit then
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
end
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates or '-' .. blacklist,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
1aq7d5meylpdjh8h4n9qane9y2wc7r5
797341
797340
2021-08-22T14:37:49Z
en>Sophivorus
0
Update to latest
797341
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1, getArg('article') )
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match( getArg(1, getArg('article') ), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local sections = not yesno( getArg('sections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
local blacklist = table.concat((config.templates or {}), ',')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates or '-' .. blacklist,
sections = sections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
popak3dwz0358mnxybk9sz9kn7u786u
797342
797341
2022-01-20T21:11:31Z
en>Sophivorus
0
Change parameter name to "subsections" per being more intuitive to its intended use
797342
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1, getArg('article') )
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match( getArg(1, getArg('article') ), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local subsections = not yesno( getArg('subsections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
local blacklist = table.concat((config.templates or {}), ',')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. "''" .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' .. "''"
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates or '-' .. blacklist,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
cemmvi2egtwqd9r3rezd8t166k7sfhd
797343
797342
2022-01-20T21:32:11Z
en>Sophivorus
0
Remove use of italics that was specific to this wiki and fix it via local stylesheet instead
797343
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1, getArg('article') )
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match( getArg(1, getArg('article') ), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local subsections = not yesno( getArg('subsections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
local blacklist = table.concat((config.templates or {}), ',')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates or '-' .. blacklist,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
bapkyy37t2vkevw0rrsg5ucn53t3zx7
797344
797343
2022-01-21T20:15:29Z
en>Sophivorus
0
Update to latest, see talk page
797344
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1, getArg('article') )
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local fragment = getArg('fragment')
local section = fragment or getArg(2, getArg('section', mw.ustring.match( getArg(1, getArg('article') ), '[^#]+#([^#]+)') ) )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references', getArg('reference', ( only == 'reference' and 1 ) ) )
local subsections = not yesno( getArg('subsections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the templates option
local blacklist = table.concat((config.blacklist or config.templates or {}), ',')
if templates then
if string.sub(templates, 1, 1) == '-' then
templates = templates .. ',' .. blacklist
end
else
templates = '-' .. blacklist
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break in case the excerpt starts with a table or list
excerpt = '\n' .. excerpt
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
lap3h4d0iqssyn9rm7th0fm8z6vb8f0
797345
797344
2022-05-18T21:14:15Z
en>Sophivorus
0
Update to latest version
797345
Scribunto
text/plain
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables
local section = getArg(2, mw.ustring.match( getArg(1), '[^#]+#(.+)') )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references')
local subsections = not yesno( getArg('subsections') )
local noBold = not yesno( getArg('bold') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the templates option from the config blacklist
local blacklist = config.blacklist and table.concat(config.blacklist, ',') or ''
if templates then
if string.sub(templates, 1, 1) == '-' then
templates = templates .. ',' .. blacklist
end
else
templates = '-' .. blacklist
end
-- Build the options for Module:Transcluder out of the template arguments and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
templates = templates,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = true,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- Add a line break before an after so that the parser interprets lists, tables, etc. correctly
excerpt = '\n' .. excerpt .. '\n'
-- If no file was found, try to excerpt one from the removed infoboxes
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
local templates = Transcluder.get(title, { only = 'templates', templates = blacklist, fixReferences = true } )
local parameters = Transcluder.getParameters(templates)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = Transcluder.removeNonFreeFiles(excerpt)
break
end
end
end
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
2dmmevva564fdsk6vpqsgo0ntl9basg
797346
797345
2022-06-01T22:04:00Z
en>Sophivorus
0
Update to 1.1, see talk page
797346
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- Version 1.1
-- License CC-BY-SA-4.0
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg(2, mw.ustring.match( getArg(1), '[^#]+#(.+)') )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references')
local subsections = not yesno( getArg('subsections') )
local noBold = not yesno( getArg('bold') )
local freefiles = yesno( getArg('freefiles') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = freefiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates(excerpt);
infobox = table.concat(infobox)
local parameters = Transcluder.getParameters(infobox)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
if ( freefiles ) then
excerpt = Transcluder.removeNonFreeFiles(excerpt)
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and (only == 'template' or only == 'templates') then
trash, excerpt = Transcluder.getTemplates(excerpt, templates);
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat(config.blacklist, ',') or ''
if templates then
if string.sub(templates, 1, 1) == '-' then
blacklist = templates .. ',' .. blacklist
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates(excerpt, blacklist);
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim(excerpt)
excerpt = string.gsub(excerpt, '\n\n\n+', '\n\n')
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
7an5kmuz709b95mdgh2qctd1ijuoev1
797347
797346
2022-06-05T13:43:46Z
en>Sophivorus
0
Update to 1.2
797347
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- Version 1.2
-- License CC-BY-SA-4.0
local Transcluder = require('Module:Transcluder')
local yesno = require('Module:Yesno')
local ok, config = pcall(require, 'Module:Excerpt/config')
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg(key, default)
value = args[key]
if value and mw.text.trim(value) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError(message, value)
if type(message) == 'string' then
message = Transcluder.getError(message, value)
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
end
return message
end
-- Helper function to get localized messages
function getMessage(key)
local ok, TNT = pcall(require, 'Module:TNT')
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
end
function p.main(frame)
args = Transcluder.parseArgs(frame)
-- Make sure the requested page exists
local page = getArg(1)
if not page then return getError('no-page') end
local title = mw.title.new(page)
if not title then return getError('no-page') end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg(2, mw.ustring.match( getArg(1), '[^#]+#(.+)') )
local hat = yesno( getArg('hat', true) )
local edit = yesno( getArg('edit', true) )
local this = getArg('this')
local only = getArg('only')
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references')
local subsections = not yesno( getArg('subsections') )
local noBold = not yesno( getArg('bold') )
local freefiles = yesno( getArg('freefiles') )
local inline = yesno( getArg('inline') )
local quote = yesno( getArg('quote') )
local more = yesno( getArg('more') )
local class = getArg('class')
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage('this')
elseif only then
hat = getMessage(only)
else
hat = getMessage('section')
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = freefiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local ok, excerpt = pcall(Transcluder.get, title, options)
if not ok then return getError(excerpt) end
if mw.text.trim(excerpt) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces('File')
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates(excerpt);
infobox = table.concat(infobox)
local parameters = Transcluder.getParameters(infobox)
local file, captions, caption
for _, pair in pairs(config.captions) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs(captions) do
if parameters[p] then caption = parameters[p] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
if ( freefiles ) then
excerpt = Transcluder.removeNonFreeFiles(excerpt)
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and (only == 'template' or only == 'templates') then
trash, excerpt = Transcluder.getTemplates(excerpt, templates);
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat(config.blacklist, ',') or ''
if templates then
if string.sub(templates, 1, 1) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates(excerpt, blacklist);
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim(excerpt)
excerpt = string.gsub(excerpt, '\n\n\n+', '\n\n')
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if inline then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
return block:node(styles):node(hat):node(excerpt):node(more)
end
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.excerpt(frame) return p.main(frame) end
return p
sgzuf1kkpk56x8im7uw6ztnfi4wkw6e
797348
797347
2022-08-05T12:50:20Z
en>Sophivorus
0
Update to 1.2.3, fixes lint errors (see talk page), global scope issue and code style changes
797348
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- Version 1.2.3
-- License CC-BY-SA-4.0
local Transcluder = require( 'Module:Transcluder' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg( key, default )
local value = args[ key ]
if value and mw.text.trim( value ) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError( message, value )
if type( message ) == 'string' then
message = Transcluder.getError( message, value )
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return message
end
-- Helper function to get localized messages
function getMessage( key )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
function p.main( frame )
args = Transcluder.parseArgs( frame )
-- Make sure the requested page exists
local page = getArg( 1 )
if not page then return getError( 'no-page' ) end
local title = mw.title.new(page)
if not title then return getError( 'no-page' ) end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg( 'this' )
local only = getArg( 'only' )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noBold = not yesno( getArg( 'bold' ) )
local freefiles = yesno( getArg( 'freefiles' ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage( 'this' )
elseif only then
hat = getMessage( only )
else
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. page
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = freefiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
if ( freefiles ) then
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create( tag1 ):addClass( 'excerpt' ):wikitext( excerpt )
if inline then
excerpt = excerpt:css( 'display', 'inline' )
end
local block = mw.html.create( tag2 ):addClass( 'excerpt-block' ):addClass( class )
if inline then
block = block:css( 'display', 'inline' )
end
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end
return p
5h3pvy13bxtuovxpxbtkayehtaqlw8g
797349
797348
2022-08-16T01:48:04Z
en>JJMC89
0
our license is 3.0
797349
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- Version 1.2.3
-- License CC BY-SA-3.0
local Transcluder = require( 'Module:Transcluder' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg( key, default )
local value = args[ key ]
if value and mw.text.trim( value ) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError( message, value )
if type( message ) == 'string' then
message = Transcluder.getError( message, value )
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return message
end
-- Helper function to get localized messages
function getMessage( key )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
function p.main( frame )
args = Transcluder.parseArgs( frame )
-- Make sure the requested page exists
local page = getArg( 1 )
if not page then return getError( 'no-page' ) end
local title = mw.title.new(page)
if not title then return getError( 'no-page' ) end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg( 'this' )
local only = getArg( 'only' )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noBold = not yesno( getArg( 'bold' ) )
local freefiles = yesno( getArg( 'freefiles' ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage( 'this' )
elseif only then
hat = getMessage( only )
else
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. page
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = freefiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
if ( freefiles ) then
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
local tag1 = 'div'
local tag2 = 'div'
if quote then
tag2 = 'blockquote'
end
excerpt = mw.html.create( tag1 ):addClass( 'excerpt' ):wikitext( excerpt )
if inline then
excerpt = excerpt:css( 'display', 'inline' )
end
local block = mw.html.create( tag2 ):addClass( 'excerpt-block' ):addClass( class )
if inline then
block = block:css( 'display', 'inline' )
end
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end
return p
8jqii8vrrp2c207nwoddkpvp5mqqfge
797350
797349
2022-09-30T14:39:16Z
en>Sophivorus
0
Update to 1.5 (non free files are now removed by default, and inline excerpts now use no tags rather than <span> tags)
797350
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- Version 1.5
-- License CC BY-SA-3.0
local Transcluder = require( 'Module:Transcluder' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg( key, default )
local value = args[ key ]
if value and mw.text.trim( value ) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError( message, value )
if type( message ) == 'string' then
message = Transcluder.getError( message, value )
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return message
end
-- Helper function to get localized messages
function getMessage( key )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
function p.main( frame )
args = Transcluder.parseArgs( frame )
-- Make sure the requested page exists
local page = getArg( 1 )
if not page then return getError( 'no-page' ) end
local title = mw.title.new(page)
if not title then return getError( 'no-page' ) end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg( 'this' )
local only = getArg( 'only' )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noLinks = not yesno( getArg( 'links', true ) )
local noBold = not yesno( getArg( 'bold' ) )
local onlyFreeFiles = yesno( getArg( 'onlyfreefiles', true ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage( 'this' )
elseif only then
hat = getMessage( only )
else
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. page
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noLinks = noLinks,
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = onlyFreeFiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
if ( onlyFreeFiles ) then
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
if inline then
return mw.text.trim( excerpt )
end
local tag = 'div'
if quote then
tag = 'blockquote'
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( class )
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end
return p
plux9uaavcv1i9pefeeu9hy76m3ls5z
797351
797350
2022-12-30T12:50:11Z
en>Sophivorus
0
Add "displaytitle" param
797351
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- Version 1.6
-- License CC BY-SA-3.0
local Transcluder = require( 'Module:Transcluder' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg( key, default )
local value = args[ key ]
if value and mw.text.trim( value ) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError( message, value )
if type( message ) == 'string' then
message = Transcluder.getError( message, value )
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return message
end
-- Helper function to get localized messages
function getMessage( key )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
function p.main( frame )
args = Transcluder.parseArgs( frame )
-- Make sure the requested page exists
local page = getArg( 1 )
if not page then return getError( 'no-page' ) end
local title = mw.title.new(page)
if not title then return getError( 'no-page' ) end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg( 'this' )
local only = getArg( 'only' )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noLinks = not yesno( getArg( 'links', true ) )
local noBold = not yesno( getArg( 'bold' ) )
local onlyFreeFiles = yesno( getArg( 'onlyfreefiles', true ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
local displaytitle = getArg( 'displaytitle' ) or page
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage( 'this' )
elseif only then
hat = getMessage( only )
else
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. displaytitle
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. displaytitle .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noLinks = noLinks,
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = onlyFreeFiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
if ( onlyFreeFiles ) then
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
if inline then
return mw.text.trim( excerpt )
end
local tag = 'div'
if quote then
tag = 'blockquote'
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( class )
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end
return p
i4wn78565teiz7ppsa9mmdlbp9c2tqw
797352
797351
2023-03-16T12:50:52Z
en>Sophivorus
0
Set enwiki as central version
797352
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation https://en.wikipedia.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- Version 1.6
-- License CC BY-SA-3.0
local Transcluder = require( 'Module:Transcluder' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg( key, default )
local value = args[ key ]
if value and mw.text.trim( value ) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError( message, value )
if type( message ) == 'string' then
message = Transcluder.getError( message, value )
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return message
end
-- Helper function to get localized messages
function getMessage( key )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
function p.main( frame )
args = Transcluder.parseArgs( frame )
-- Make sure the requested page exists
local page = getArg( 1 )
if not page then return getError( 'no-page' ) end
local title = mw.title.new(page)
if not title then return getError( 'no-page' ) end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg( 'this' )
local only = getArg( 'only' )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noLinks = not yesno( getArg( 'links', true ) )
local noBold = not yesno( getArg( 'bold' ) )
local onlyFreeFiles = yesno( getArg( 'onlyfreefiles', true ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
local displaytitle = getArg( 'displaytitle' ) or page
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage( 'this' )
elseif only then
hat = getMessage( only )
else
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. displaytitle
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. displaytitle .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noLinks = noLinks,
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = onlyFreeFiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
if ( onlyFreeFiles ) then
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
if inline then
return mw.text.trim( excerpt )
end
local tag = 'div'
if quote then
tag = 'blockquote'
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( class )
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end
return p
l0zcpmae9tsl49bgux28wy3ut7hxt11
797353
797352
2023-03-22T21:41:18Z
en>Sophivorus
0
Add briefdates feature
797353
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-3.0
local Transcluder = require( 'Module:Transcluder' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
function getArg( key, default )
local value = args[ key ]
if value and mw.text.trim( value ) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
function getError( message, value )
if type( message ) == 'string' then
message = Transcluder.getError( message, value )
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return message
end
-- Helper function to get localized messages
function getMessage( key )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
function p.main( frame )
args = Transcluder.parseArgs( frame )
-- Make sure the requested page exists
local page = getArg( 1 )
if not page then return getError( 'no-page' ) end
local title = mw.title.new(page)
if not title then return getError( 'no-page' ) end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg( 'this' )
local only = getArg( 'only' )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noLinks = not yesno( getArg( 'links', true ) )
local noBold = not yesno( getArg( 'bold' ) )
local onlyFreeFiles = yesno( getArg( 'onlyfreefiles', true ) )
local briefDates = yesno( getArg( 'briefdates', false ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
local displaytitle = getArg( 'displaytitle' ) or page
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage( 'this' )
elseif only then
hat = getMessage( only )
else
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. displaytitle
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. displaytitle .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noLinks = noLinks,
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = onlyFreeFiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
-- Fix birth and death dates, but only in the first paragraph
if briefDates then
local startpos = 1 -- skip initial templates
local s
local e = 0
repeat
startpos = e + 1
s, e = mw.ustring.find( excerpt, "%s*%b{}%s*", startpos )
until not s or s > startpos
s, e = mw.ustring.find( excerpt, "%b()", startpos ) -- get (...), which may be (year–year)
if s and s < startpos + 100 then -- look only near the start
local year1, conjunction, year2 = mw.ustring.match( mw.ustring.sub( excerpt, s, e ), '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and (mw.ustring.match( conjunction, '[%-–—]' ) or mw.ustring.match( conjunction, '{{%s*[sS]nd%s*}}' )) then
local y1 = tonumber(year1)
local y2 = tonumber(year2)
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( "%Y" )) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. "–" .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
if ( onlyFreeFiles ) then
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
if inline then
return mw.text.trim( excerpt )
end
local tag = 'div'
if quote then
tag = 'blockquote'
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( class )
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end
return p
25rhq28iilv3arevy4fyzt5ybuz36mh
797354
797353
2023-07-10T16:17:53Z
en>Sophivorus
0
Update to latest: refine error handling for invalid titles, make some methods local, add Aidan9382 as author, remove old reference to fragment param
797354
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local Transcluder = require( 'Module:Transcluder' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
local function getArg( key, default )
local value = args[ key ]
if value and mw.text.trim( value ) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
local function getError( message, value )
if type( message ) == 'string' then
message = Transcluder.getError( message, value )
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return message
end
-- Helper function to get localized messages
local function getMessage( key )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
-- Main entry point for templates
function p.main( frame )
args = Transcluder.parseArgs( frame )
-- Make sure the requested page exists
local page = getArg( 1 )
if not page or page == '{{{1}}}' then return getError( 'no-page' ) end
local title = mw.title.new(page)
if not title then return getError( 'invalid-title', page ) end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg( 'this' )
local only = getArg( 'only' )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noLinks = not yesno( getArg( 'links', true ) )
local noBold = not yesno( getArg( 'bold' ) )
local onlyFreeFiles = yesno( getArg( 'onlyfreefiles', true ) )
local briefDates = yesno( getArg( 'briefdates', false ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
local displaytitle = getArg( 'displaytitle' ) or page
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage( 'this' )
elseif only then
hat = getMessage( only )
else
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. displaytitle
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. displaytitle .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noLinks = noLinks,
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = onlyFreeFiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
-- Fix birth and death dates, but only in the first paragraph
if briefDates then
local startpos = 1 -- skip initial templates
local s
local e = 0
repeat
startpos = e + 1
s, e = mw.ustring.find( excerpt, "%s*%b{}%s*", startpos )
until not s or s > startpos
s, e = mw.ustring.find( excerpt, "%b()", startpos ) -- get (...), which may be (year–year)
if s and s < startpos + 100 then -- look only near the start
local year1, conjunction, year2 = mw.ustring.match( mw.ustring.sub( excerpt, s, e ), '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and (mw.ustring.match( conjunction, '[%-–—]' ) or mw.ustring.match( conjunction, '{{%s*[sS]nd%s*}}' )) then
local y1 = tonumber(year1)
local y2 = tonumber(year2)
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( "%Y" )) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. "–" .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
if ( onlyFreeFiles ) then
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
if inline then
return mw.text.trim( excerpt )
end
local tag = 'div'
if quote then
tag = 'blockquote'
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( class )
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end
return p
otwcbfmbi3hwmrd1unz7dux28iop4li
797355
797354
2025-01-28T01:57:40Z
en>Arthurfragoso
0
Use infobox CSS class if it exists (mainly for dark mode compatibility)
797355
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local Transcluder = require( 'Module:Transcluder' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local p = {}
-- Helper function to get arguments
local args
local function getArg( key, default )
local value = args[ key ]
if value and mw.text.trim( value ) ~= '' then
return value
end
return default
end
-- Helper function to handle errors
local function getError( message, value )
if type( message ) == 'string' then
message = Transcluder.getError( message, value )
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return message
end
-- Helper function to get localized messages
local function getMessage( key )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
-- Main entry point for templates
function p.main( frame )
args = Transcluder.parseArgs( frame )
-- Make sure the requested page exists
local page = getArg( 1 )
if not page or page == '{{{1}}}' then return getError( 'no-page' ) end
local title = mw.title.new(page)
if not title then return getError( 'invalid-title', page ) end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
-- Set variables from the template parameters
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg( 'this' )
local only = getArg( 'only' )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noLinks = not yesno( getArg( 'links', true ) )
local noBold = not yesno( getArg( 'bold' ) )
local onlyFreeFiles = yesno( getArg( 'onlyfreefiles', true ) )
local briefDates = yesno( getArg( 'briefdates', false ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
local displaytitle = getArg( 'displaytitle' ) or page
-- Build the hatnote
if hat and not inline then
if this then
hat = this
elseif quote then
hat = getMessage( 'this' )
elseif only then
hat = getMessage( only )
else
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. displaytitle
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. displaytitle .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
-- Build the "Read more" link
if more and not inline then
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
more = nil
end
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local options = {
files = files,
lists = lists,
tables = tables,
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noLinks = noLinks,
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = onlyFreeFiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
-- Fix birth and death dates, but only in the first paragraph
if briefDates then
local startpos = 1 -- skip initial templates
local s
local e = 0
repeat
startpos = e + 1
s, e = mw.ustring.find( excerpt, "%s*%b{}%s*", startpos )
until not s or s > startpos
s, e = mw.ustring.find( excerpt, "%b()", startpos ) -- get (...), which may be (year–year)
if s and s < startpos + 100 then -- look only near the start
local year1, conjunction, year2 = mw.ustring.match( mw.ustring.sub( excerpt, s, e ), '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and (mw.ustring.match( conjunction, '[%-–—]' ) or mw.ustring.match( conjunction, '{{%s*[sS]nd%s*}}' )) then
local y1 = tonumber(year1)
local y2 = tonumber(year2)
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( "%Y" )) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. "–" .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
end
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
then
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption, cssclasses, cssclass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssclasses = pair[3]
for _, p in pairs(cssclasses) do
if parameters[p] then
cssclass = ((parameters[p] == 'skin-invert') and 'skin-invert-image' or parameters[p])
break
end
end
end
excerpt = '[[File:' .. file ..
(cssclass and ('|class=' .. cssclass) or '') ..
'|thumb|' .. (caption or '') .. ']]' .. excerpt
if ( onlyFreeFiles ) then
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
break
end
end
end
-- Unlike other elements, templates are filtered here
-- because we had to search the infoboxes for files
local trash
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
blacklist = templates
end
else
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
-- Add tracking categories
if config.categories then
local contentCategory = config.categories.content
if contentCategory and mw.title.getCurrentTitle().isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
end
-- Load the styles
local styles
if config.styles then
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
end
-- Combine and return the elements
if inline then
return mw.text.trim( excerpt )
end
local tag = 'div'
if quote then
tag = 'blockquote'
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( class )
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end
return p
3iae3gnikt17y7anu02zdv3ie3mcivc
797356
797355
2025-10-02T13:24:14Z
en>Sophivorus
0
Update to latest version
797356
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, t in pairs( tables ) do
local id = string.match( t, '{|[^\n]-id%s*=%s*["\']?([^"\'\n]+)["\']?[^\n]*\n' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, t )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start = 1 -- skip initial templates
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt, page )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "()'''(.-'*)'''", function ( a, b )
if not mw.ustring.find( b, '%[' ) and not mw.ustring.find( b, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. b .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
local match = {}
for i = 1, #list do
match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
local ok2, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok2 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
local filter = {cache = {}, terms = filters}
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
jgd9kvsiee9c9ouo5xxzek9fb6zwqe8
797357
797356
2025-10-06T13:09:35Z
en>Sophivorus
0
Make matchFilter account for nil filters, use WikitextParser to get table ids, and remove unnecessary code
797357
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start = 1 -- skip initial templates
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
local match = {}
for i = 1, #list do
match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
local ok2, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok2 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
local filter = {cache = {}, terms = filters}
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
rnq76uv86a6rsvb7gbdoa5jldh3wc2d
797358
797357
2025-10-10T12:22:12Z
en>Sophivorus
0
Add param to skip tracking categories
797358
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
trackingCategories = yesno( Excerpt.getArg( 'trackingCategories', true ) ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if params.trackingCategories and config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start = 1 -- skip initial templates
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
local match = {}
for i = 1, #list do
match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
local ok2, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok2 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
local filter = {cache = {}, terms = filters}
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
bdetulq45l9x0xj3wirpobi2tsdyau5
797359
797358
2025-10-21T12:43:57Z
en>Sophivorus
0
Rename "trackingCategories" param to "track"
797359
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
track = yesno( Excerpt.getArg( 'track', true ) ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if params.track and config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start = 1 -- skip initial templates
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
local match = {}
for i = 1, #list do
match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
local ok2, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok2 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
local filter = {cache = {}, terms = filters}
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
chuepw1c3inkl98a1e82d15szf6b0ll
797360
797359
2025-12-23T17:27:54Z
en>Aidan9382
0
Ensure that the padding newline is retained when tracking categories are added
797360
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
track = yesno( Excerpt.getArg( 'track', true ) ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if params.track and config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start = 1 -- skip initial templates
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local addedCategories = false
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
if addedCategories then
excerpt = excerpt .. '\n'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
local match = {}
for i = 1, #list do
match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
local ok2, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok2 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
local filter = {cache = {}, terms = filters}
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
ka2e1ezo2sdes03rw91k5qedbiixxgj
797361
797360
2026-01-17T13:43:34Z
en>Sophivorus
0
Fix minor Lua linting errors
797361
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
track = yesno( Excerpt.getArg( 'track', true ) ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if params.track and config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start = 1 -- skip initial templates
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local addedCategories = false
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
if addedCategories then
excerpt = excerpt .. '\n'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
for i = 1, #list do
local match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
local ok2, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok2 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
filter = { cache = {}, terms = filters }
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
o9drtvsollvdhv0jwgian5wsaz75j92
797362
797361
2026-01-17T17:57:51Z
en>Sophivorus
0
Fix bug where tables and lists are not parsed correctly when an infobox file is prepended
797362
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
track = yesno( Excerpt.getArg( 'track', true ) ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if params.track and config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]\n' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start = 1 -- skip initial templates
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local addedCategories = false
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
if addedCategories then
excerpt = excerpt .. '\n'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
for i = 1, #list do
local match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
local ok2, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok2 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
filter = { cache = {}, terms = filters }
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
sqwxoaaqa8kkfuqqm9rxo7n75q6qxtx
797363
797362
2026-01-18T16:48:00Z
en>Sophivorus
0
Fix lint errors
797363
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
track = yesno( Excerpt.getArg( 'track', true ) ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if params.track and config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]\n' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local addedCategories = false
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
if addedCategories then
excerpt = excerpt .. '\n'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
for i = 1, #list do
local match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok2, TNT = pcall( require, 'Module:TNT' )
if not ok2 then return key end
local ok3, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok3 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
filter = { cache = {}, terms = filters }
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
14inzvnv7781xy5025bzi28282zvfdw
797364
797363
2026-03-06T12:56:17Z
en>Sophivorus
0
Add option to only get the filename of the first file, see [[Module talk:Excerpt#Feature request for file excerpts]]
797364
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
track = yesno( Excerpt.getArg( 'track', true ) ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or params.only == 'filename' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
if params.only == 'filename' then
local files = parser.getFiles( excerpt )
local file = files[1]
excerpt = file and parser.getFileName( file ) or 'Noimage.png'
params.inline = true
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if params.track and config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]\n' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local addedCategories = false
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
if addedCategories then
excerpt = excerpt .. '\n'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
for i = 1, #list do
local match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok2, TNT = pcall( require, 'Module:TNT' )
if not ok2 then return key end
local ok3, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok3 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
filter = { cache = {}, terms = filters }
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
f13qcb3mtjeccbzr0i5jmt33ddc570f
797365
797364
2026-06-08T20:26:19Z
SM7
3953
197 revisions imported from [[:en:Module:Excerpt]]
797364
Scribunto
text/plain
-- Module:Excerpt implements the Excerpt template
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
local parser = require( 'Module:WikitextParser' )
local yesno = require( 'Module:Yesno' )
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
local Excerpt = {}
-- Main entry point for templates
function Excerpt.main( frame )
-- Make sure the requested page exists and get the wikitext
local page = Excerpt.getArg( 1 )
if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end
local title = mw.title.new( page )
if not title then return Excerpt.getError( 'invalid-title', page ) end
local fragment = title.fragment -- save for later
if title.isRedirect then
title = title.redirectTarget
if fragment == "" then
fragment = title.fragment -- page merge potential
end
end
if not title.exists then return Excerpt.getError( 'page-not-found', page ) end
page = title.prefixedText
local wikitext = title:getContent()
-- Get the template params and process them
local params = {
hat = yesno( Excerpt.getArg( 'hat', true ) ),
this = Excerpt.getArg( 'this' ),
only = Excerpt.getArg( 'only' ),
files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ),
lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ),
tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ),
templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ),
paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ),
references = yesno( Excerpt.getArg( 'references', true ) ),
subsections = yesno( Excerpt.getArg( 'subsections', false ) ),
links = yesno( Excerpt.getArg( 'links', true ) ),
bold = yesno( Excerpt.getArg( 'bold', false ) ),
briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ),
inline = yesno( Excerpt.getArg( 'inline' ) ),
quote = yesno( Excerpt.getArg( 'quote' ) ),
more = yesno( Excerpt.getArg( 'more' ) ),
class = Excerpt.getArg( 'class' ),
track = yesno( Excerpt.getArg( 'track', true ) ),
displayTitle = Excerpt.getArg( 'displaytitle', page ),
}
-- Make sure the requested section exists and get the excerpt
local excerpt
local section = Excerpt.getArg( 2, fragment )
section = mw.text.trim( section )
if section == '' then section = nil end
if section then
excerpt = parser.getSectionTag( wikitext, section )
if not excerpt then
if params.subsections then
excerpt = parser.getSection( wikitext, section )
else
local sections = parser.getSections( wikitext )
excerpt = sections[ section ]
end
end
if not excerpt then return Excerpt.getError( 'section-not-found', section ) end
if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end
else
excerpt = parser.getLead( wikitext )
if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end
end
-- Remove noinclude bits
excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' )
-- Filter various elements from the excerpt
excerpt = Excerpt.filterFiles( excerpt, params.files )
excerpt = Excerpt.filterLists( excerpt, params.lists )
excerpt = Excerpt.filterTables( excerpt, params.tables )
excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs )
-- If no file is found, try to get one from the infobox
if ( params.only == 'file' or params.only == 'files' or params.only == 'filename' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files
and not section -- and we're in the lead section
and config.captions -- and we have the config option required to try finding files in infoboxes
and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt
then
excerpt = Excerpt.addInfoboxFile( excerpt )
end
-- Filter the templates by appending the templates blacklist to the templates filter
if config.blacklist then
local blacklist = table.concat( config.blacklist, ',' )
if params.templates then
if string.sub( params.templates, 1, 1 ) == '-' then
params.templates = params.templates .. ',' .. blacklist
end
else
params.templates = '-' .. blacklist
end
end
excerpt = Excerpt.filterTemplates( excerpt, params.templates )
-- Leave only the requested elements
if params.only == 'file' or params.only == 'files' then
local files = parser.getFiles( excerpt )
excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' )
end
if params.only == 'list' or params.only == 'lists' then
local lists = parser.getLists( excerpt )
excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' )
end
if params.only == 'table' or params.only == 'tables' then
local tables = parser.getTables( excerpt )
excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' )
end
if params.only == 'paragraph' or params.only == 'paragraphs' then
local paragraphs = parser.getParagraphs( excerpt )
excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' )
end
if params.only == 'template' or params.only == 'templates' then
local templates = parser.getTemplates( excerpt )
excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' )
end
if params.only == 'filename' then
local files = parser.getFiles( excerpt )
local file = files[1]
excerpt = file and parser.getFileName( file ) or 'Noimage.png'
params.inline = true
end
-- @todo Make more robust and move downwards
if params.briefDates then
excerpt = Excerpt.fixDates( excerpt )
end
-- Remove unwanted elements
excerpt = Excerpt.removeComments( excerpt )
excerpt = Excerpt.removeSelfLinks( excerpt )
excerpt = Excerpt.removeNonFreeFiles( excerpt )
excerpt = Excerpt.removeBehaviorSwitches( excerpt )
-- Fix or remove the references
if params.references then
excerpt = Excerpt.fixReferences( excerpt, page, wikitext )
else
excerpt = Excerpt.removeReferences( excerpt )
end
-- Remove wikilinks
if not params.links then
excerpt = Excerpt.removeLinks( excerpt )
end
-- Link the bold text near the start of most leads and then remove it
if not section then
excerpt = Excerpt.linkBold( excerpt, page )
end
if not params.bold then
excerpt = Excerpt.removeBold( excerpt )
end
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = excerpt:gsub( '\n\n\n+', '\n\n' )
excerpt = mw.text.trim( excerpt )
excerpt = '\n' .. excerpt .. '\n'
-- Remove nested categories
excerpt = frame:preprocess( excerpt )
excerpt = Excerpt.removeCategories( excerpt )
-- Add tracking categories
if params.track and config.categories then
excerpt = Excerpt.addTrackingCategories( excerpt )
end
-- Build the final output
if params.inline then
return mw.text.trim( excerpt )
end
local tag = params.quote and 'blockquote' or 'div'
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class )
if config.styles then
local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
block:node( styles )
end
if params.hat then
local hat = Excerpt.getHat( page, section, params )
block:node( hat )
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
block:node( excerpt )
if params.more then
local more = Excerpt.getReadMore( page, section )
block:node( more )
end
return block
end
-- Filter the files in the given wikitext against the given filter
function Excerpt.filterFiles( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local files = parser.getFiles( wikitext )
for index, file in pairs( files ) do
local name = parser.getFileName( file )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
return wikitext
end
-- Filter the lists in the given wikitext against the given filter
function Excerpt.filterLists( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local lists = parser.getLists( wikitext )
for index, list in pairs( lists ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, list )
end
end
return wikitext
end
-- Filter the tables in the given wikitext against the given filter
function Excerpt.filterTables( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local tables = parser.getTables( wikitext )
for index, tableWikitext in pairs( tables ) do
local id = parser.getTableAttribute( tableWikitext, 'id' )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then
wikitext = Excerpt.removeString( wikitext, tableWikitext )
end
end
return wikitext
end
-- Filter the paragraphs in the given wikitext against the given filter
function Excerpt.filterParagraphs( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local paragraphs = parser.getParagraphs( wikitext )
for index, paragraph in pairs( paragraphs ) do
if isBlacklist and Excerpt.matchFilter( index, filters )
or not isBlacklist and not Excerpt.matchFilter( index, filters ) then
wikitext = Excerpt.removeString( wikitext, paragraph )
end
end
return wikitext
end
-- Filter the templates in the given wikitext against the given filter
function Excerpt.filterTemplates( wikitext, filter )
if not filter then return wikitext end
local filters, isBlacklist = Excerpt.parseFilter( filter )
local templates = parser.getTemplates( wikitext )
for index, template in pairs( templates ) do
local name = parser.getTemplateName( template )
if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) )
or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then
wikitext = Excerpt.removeString( wikitext, template )
end
end
return wikitext
end
function Excerpt.addInfoboxFile( excerpt )
-- We cannot distinguish the infobox from the other templates, so we search them all
local templates = parser.getTemplates( excerpt )
for _, template in pairs( templates ) do
local parameters = parser.getTemplateParameters( template )
local file, captions, caption, cssClasses, cssClass
for _, pair in pairs( config.captions ) do
file = pair[1]
file = parameters[file]
if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = string.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
for _, p in pairs( captions ) do
if parameters[ p ] then caption = parameters[ p ] break end
end
-- Check for CSS classes
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssClasses = pair[3]
for _, p in pairs( cssClasses ) do
if parameters[ p ] then
cssClass = ( parameters[ p ] == 'skin-invert' ) and 'skin-invert-image' or parameters[ p ]
break
end
end
end
local class = cssClass and ( '|class=' .. cssClass ) or ''
return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]\n' .. excerpt
end
end
end
return excerpt
end
function Excerpt.removeNonFreeFiles( wikitext )
local files = parser.getFiles( wikitext )
for _, file in pairs( files ) do
local fileName = 'File:' .. parser.getFileName( file )
local fileTitle = mw.title.new( fileName )
if fileTitle then
local fileDescription = fileTitle:getContent()
if not fileDescription or fileDescription == '' then
local frame = mw.getCurrentFrame()
fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons
end
if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then
wikitext = Excerpt.removeString( wikitext, file )
end
end
end
return wikitext
end
function Excerpt.getHat( page, section, params )
local hat
-- Build the text
if params.this then
hat = params.this
elseif params.quote then
hat = Excerpt.getMessage( 'this' )
elseif params.only then
hat = Excerpt.getMessage( params.only )
else
hat = Excerpt.getMessage( 'section' )
end
hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' )
-- Build the link
if section then
hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle
.. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].'
end
-- Build the edit link
local title = mw.title.new( page )
local editUrl = title:fullUrl( 'action=edit' )
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
if config.hat then
local frame = mw.getCurrentFrame()
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
return hat
end
function Excerpt.getReadMore( page, section )
local link = "'''[[" .. page
if section then
link = link .. '#' .. section
end
local text = Excerpt.getMessage( 'more' )
link = link .. '|' .. text .. "]]'''"
link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link )
return link
end
-- Fix birth and death dates, but only in the first paragraph
-- @todo Use parser.getParagraphs() to get the first paragraph
function Excerpt.fixDates( excerpt )
local start
local s
local e = 0
repeat
start = e + 1
s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start )
until not s or s > start
s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year)
if s and s < start + 100 then -- look only near the start
local excerptStart = mw.ustring.sub( excerpt, s, e )
local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.match( conjunction, '{{%s*[sS]nd%s*}}' ) ) then
local y1 = tonumber( year1 )
local y2 = tonumber( year2 )
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( '%Y' ) ) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. '–' .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
return excerpt
end
-- Replace the first call to each reference defined outside of the excerpt for the full reference, to prevent undefined references
-- Then prefix the page title to the reference names to prevent conflicts
-- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo">
-- and also <ref name="Foo" /> for <ref name="Title of the article Foo" />
-- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo">
-- and <ref group="Bar"> for <ref>
-- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book">
function Excerpt.fixReferences( excerpt, page, wikitext )
local references = parser.getReferences( excerpt )
local fixed = {}
for _, reference in pairs( references ) do
local name = parser.getTagAttribute( reference, 'name' )
if not fixed[ name ] then -- fix each reference only once
local content = parser.getTagContent( reference )
if not content then -- reference is self-closing
local full = parser.getReference( excerpt, name )
if not full then -- the reference is not defined in the excerpt
full = parser.getReference( wikitext, name )
if full then
excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 )
end
table.insert( fixed, name )
end
end
end
end
-- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' )
-- Remove reference groups because they don't apply to the transcluding page
excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' )
return excerpt
end
function Excerpt.removeReferences( excerpt )
local references = parser.getReferences( excerpt )
for _, reference in pairs( references ) do
excerpt = Excerpt.removeString( excerpt, reference )
end
return excerpt
end
function Excerpt.removeCategories( excerpt )
local categories = parser.getCategories( excerpt )
for _, category in pairs( categories ) do
excerpt = Excerpt.removeString( excerpt, category )
end
return excerpt
end
function Excerpt.removeBehaviorSwitches( excerpt )
return excerpt:gsub( '__[A-Z]+__', '' )
end
function Excerpt.removeComments( excerpt )
return excerpt:gsub( '<!%-%-.-%-%->', '' )
end
function Excerpt.removeBold( excerpt )
return excerpt:gsub( "'''", '' )
end
function Excerpt.removeLinks( excerpt )
local links = parser.getLinks( excerpt )
for _, link in pairs( links ) do
excerpt = Excerpt.removeString( excerpt, link )
end
return excerpt
end
-- @todo Use parser.getLinks
function Excerpt.removeSelfLinks( excerpt )
local lang = mw.language.getContentLanguage()
local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText )
local ucpage = lang:ucfirst( page )
local lcpage = lang:lcfirst( page )
excerpt = excerpt
:gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' )
:gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' )
:gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' )
:gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' )
return excerpt
end
-- Replace the bold title or synonym near the start of the page by a link to the page
function Excerpt.linkBold( excerpt, page )
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves
if position then
local length = mw.ustring.len( page )
excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, position + length + 3, -1 ) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text )
if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template
return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]'''
else
return nil -- instruct gsub to make no change
end
end, 1 ) -- terminates the anonymous replacement function passed to gsub
end
return excerpt
end
function Excerpt.addTrackingCategories( excerpt )
local currentTitle = mw.title.getCurrentTitle()
local addedCategories = false
local contentCategory = config.categories.content
if contentCategory and currentTitle.isContentPage then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
end
local namespaceCategory = config.categories[ currentTitle.namespace ]
if namespaceCategory then
addedCategories = true
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
end
if addedCategories then
excerpt = excerpt .. '\n'
end
return excerpt
end
-- Helper method to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
function Excerpt.matchAny( text, pre, list, post, init )
for i = 1, #list do
local match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) }
if match[1] then return unpack( match ) end
end
return nil
end
-- Helper function to get arguments
-- args from Lua calls have priority over parent args from template
function Excerpt.getArg( key, default )
local frame = mw.getCurrentFrame()
for k, value in pairs( frame:getParent().args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
for k, value in pairs( frame.args ) do
if k == key and mw.text.trim( value ) ~= '' then
return value
end
end
return default
end
-- Helper method to get an error message
-- This method also categorizes the current page in one of the configured error categories
function Excerpt.getError( key, value )
local message = Excerpt.getMessage( 'error-' .. key, value )
local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message )
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
markup:node( '[[Category:' .. config.categories.errors .. ']]' )
end
return markup
end
-- Helper method to get a localized message
-- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab
-- If Module:TNT is not available or the localized message does not exist, the key is returned instead
function Excerpt.getMessage( key, value )
local ok2, TNT = pcall( require, 'Module:TNT' )
if not ok2 then return key end
local ok3, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value )
if not ok3 then return key end
return message
end
-- Helper method to escape a string for use in regexes
function Excerpt.escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper method to remove a string from a text
-- @param text Text from where to remove the string
-- @param str String to remove
-- @return The given text with the string removed
function Excerpt.removeString( text, str )
local pattern = Excerpt.escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) )
end
return text:gsub( pattern, '' )
end
-- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the filters should be treated as a blacklist or not
-- @note Merging this into matchFilter is possible, but way too inefficient
function Excerpt.parseFilter( filter )
local filters = {}
local isBlacklist = false
if string.sub( filter, 1, 1 ) == '-' then
isBlacklist = true
filter = string.sub( filter, 2 )
end
local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'}
for _, value in pairs( values ) do
value = mw.text.trim( value )
local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do filters[ i ] = true end
else
filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
filter = { cache = {}, terms = filters }
return filter, isBlacklist
end
-- Helper function to see if a value matches any of the given filters
function Excerpt.matchFilter( value, filter )
if value == nil then
return false
elseif type(value) == "number" then
return filter.terms[value]
else
local cached = filter.cache[value]
if cached ~= nil then
return cached
end
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for term in pairs( filter.terms ) do
if value == tostring(term)
or type(term) == "string" and (
lcvalue == term
or ucvalue == term
or mw.ustring.match( value, term )
) then
filter.cache[value] = true
return true
end
end
filter.cache[value] = false
end
end
return Excerpt
f13qcb3mtjeccbzr0i5jmt33ddc570f
Module:WikitextParser
828
101021
797366
2024-02-22T14:26:25Z
en>Sophivorus
0
[[WP:AES|←]]Created page with '-- Module:WikitextParser is a general-purpose wikitext parser -- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser -- Authors: User:Sophivorus, User:Certes & others -- License: CC-BY-SA-4.0 local WikitextParser = {} -- Get the requested tags from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @param selector Tags to return, for example 'div' or 'div,span,gallery'. Omit to return all tags. -- @retu...'
797366
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get the requested tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tags to return, for example 'div' or 'div,span,gallery'. Omit to return all tags.
-- @return Sequence of strings containing the wikitext of the requested tags.
-- @return Original wikitext minus requested tags.
function WikitextParser.getTags( wikitext, selector )
local tags = {}
local tagName, tagText, tagEnd
local original = wikitext
local count = 0
for tagStart, tagOpen in string.gmatch( original, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tagText = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len(tagOpen) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( original, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tagText = string.sub( original, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tagText = string.sub( original, tagStart, tagEnd )
end
count = count + 1
if isSelected( selector, count, tagName ) then
table.insert( tags, tagText )
else
wikitext = removeString( wikitext, tagText )
end
end
return tags, wikitext
end
-- Helper function to determine if a given element is filtered or not by a selector
-- @todo Should probably merge with parseSelector
local function isSelected( selector, count, value )
local map, blacklist = parseSelector( selector )
if not blacklist and ( not map or map[ count ] or map[ value ] )
or blacklist and map and not map[ count ] and not map[ value ] then
return true
end
end
-- Helper function to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param selector Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the selector should be treated as a blacklist or not
local function parseSelector( selector )
local map = {}
local blacklist = false
if not selector then return nil, false end
if type( selector ) == 'number' then
if selector < 0 then
selector = -selector
blacklist = true
end
map = { [ selector ] = true }
elseif type( selector ) == 'string' then
if string.sub( selector, 1, 1 ) == '-' then
selector = string.sub( selector, 2 )
blacklist = true
end
local ranges = mw.text.split( value, ',' ) -- split ranges: '1,3-5' to {'1','3-5'}
for _, range in pairs( ranges ) do
range = mw.text.trim( range )
local min, max = mw.ustring.match( range, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( range, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do map[ i ] = true end
else
map[ range ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
-- List has the form { [1] = false, [2] = true, ['c'] = false }
-- Convert it to { [1] = true, [2] = true, ['c'] = true }
-- But if ANY value is set to false, treat the list as a blacklist
elseif type( selector ) == 'table' then
for i, v in pairs( selector ) do
if v == false then blacklist = true end
map[ i ] = true
end
end
return map, blacklist
end
-- Helper function to remove a string from a text
local function removeString( text, str )
local pattern = escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. escapeString( mw.ustring.sub( str, -999 ) )
end
return string.gsub( text, pattern, '' )
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
8k8815oycamkzykzm6p8k3wy86olvap
797367
797366
2024-02-22T14:33:41Z
en>Sophivorus
0
797367
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper function to remove a string from a text
local function removeString( text, str )
local pattern = escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. escapeString( mw.ustring.sub( str, -999 ) )
end
return string.gsub( text, pattern, '' )
end
-- Helper function to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param selector Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the selector should be treated as a blacklist or not
local function parseSelector( selector )
local map = {}
local blacklist = false
if not selector then return nil, false end
if type( selector ) == 'number' then
if selector < 0 then
selector = -selector
blacklist = true
end
map = { [ selector ] = true }
elseif type( selector ) == 'string' then
if string.sub( selector, 1, 1 ) == '-' then
selector = string.sub( selector, 2 )
blacklist = true
end
local ranges = mw.text.split( value, ',' ) -- split ranges: '1,3-5' to {'1','3-5'}
for _, range in pairs( ranges ) do
range = mw.text.trim( range )
local min, max = mw.ustring.match( range, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( range, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do map[ i ] = true end
else
map[ range ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
-- List has the form { [1] = false, [2] = true, ['c'] = false }
-- Convert it to { [1] = true, [2] = true, ['c'] = true }
-- But if ANY value is set to false, treat the list as a blacklist
elseif type( selector ) == 'table' then
for i, v in pairs( selector ) do
if v == false then blacklist = true end
map[ i ] = true
end
end
return map, blacklist
end
-- Helper function to determine if a given element is filtered or not by a selector
-- @todo Should probably merge with parseSelector
local function isSelected( selector, count, value )
local map, blacklist = parseSelector( selector )
if not blacklist and ( not map or map[ count ] or map[ value ] )
or blacklist and map and not map[ count ] and not map[ value ] then
return true
end
end
-- Get the requested tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tags to return, for example 'div' or 'div,span,gallery'. Omit to return all tags.
-- @return Sequence of strings containing the wikitext of the requested tags.
-- @return Original wikitext minus requested tags.
function WikitextParser.getTags( wikitext, selector )
local tags = {}
local tagName, tagText, tagEnd
local original = wikitext
local count = 0
for tagStart, tagOpen in string.gmatch( original, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tagText = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len(tagOpen) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( original, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tagText = string.sub( original, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tagText = string.sub( original, tagStart, tagEnd )
end
count = count + 1
if isSelected( selector, count, tagName ) then
table.insert( tags, tagText )
else
wikitext = removeString( wikitext, tagText )
end
end
return tags, wikitext
end
return WikitextParser
avhmzztnap0n6ji4ed1ml2wbiezx4zs
797368
797367
2024-02-22T14:35:14Z
en>Sophivorus
0
Fix variable name
797368
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper function to remove a string from a text
local function removeString( text, str )
local pattern = escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. escapeString( mw.ustring.sub( str, -999 ) )
end
return string.gsub( text, pattern, '' )
end
-- Helper function to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param selector Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the selector should be treated as a blacklist or not
local function parseSelector( selector )
local map = {}
local blacklist = false
if not selector then return nil, false end
if type( selector ) == 'number' then
if selector < 0 then
selector = -selector
blacklist = true
end
map = { [ selector ] = true }
elseif type( selector ) == 'string' then
if string.sub( selector, 1, 1 ) == '-' then
selector = string.sub( selector, 2 )
blacklist = true
end
local ranges = mw.text.split( selector, ',' ) -- split ranges: '1,3-5' to {'1','3-5'}
for _, range in pairs( ranges ) do
range = mw.text.trim( range )
local min, max = mw.ustring.match( range, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( range, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do map[ i ] = true end
else
map[ range ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
-- List has the form { [1] = false, [2] = true, ['c'] = false }
-- Convert it to { [1] = true, [2] = true, ['c'] = true }
-- But if ANY value is set to false, treat the list as a blacklist
elseif type( selector ) == 'table' then
for i, v in pairs( selector ) do
if v == false then blacklist = true end
map[ i ] = true
end
end
return map, blacklist
end
-- Helper function to determine if a given element is filtered or not by a selector
-- @todo Should probably merge with parseSelector
local function isSelected( selector, count, value )
local map, blacklist = parseSelector( selector )
if not blacklist and ( not map or map[ count ] or map[ value ] )
or blacklist and map and not map[ count ] and not map[ value ] then
return true
end
end
-- Get the requested tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tags to return, for example 'div' or 'div,span,gallery'. Omit to return all tags.
-- @return Sequence of strings containing the wikitext of the requested tags.
-- @return Original wikitext minus requested tags.
function WikitextParser.getTags( wikitext, selector )
local tags = {}
local tagName, tagText, tagEnd
local original = wikitext
local count = 0
for tagStart, tagOpen in string.gmatch( original, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tagText = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len(tagOpen) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( original, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tagText = string.sub( original, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tagText = string.sub( original, tagStart, tagEnd )
end
count = count + 1
if isSelected( selector, count, tagName ) then
table.insert( tags, tagText )
else
wikitext = removeString( wikitext, tagText )
end
end
return tags, wikitext
end
return WikitextParser
c5swnkuv2ypsja4mqh5ue6suzwit23i
797369
797368
2024-02-22T14:48:34Z
en>Sophivorus
0
Merge parseSelector and isSelected into inSelector and generalize
797369
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper function to remove a string from a text
local function removeString( text, str )
local pattern = escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. escapeString( mw.ustring.sub( str, -999 ) )
end
return string.gsub( text, pattern, '' )
end
-- Helper function to determine if the given value is in the given selector or not
-- @param selector Comma-separated list of values, for example 'div,span' or '1,3-5'
-- @param value Value to test, for example 'div' or 1
-- @return boolean
local function inSelector( selector, value )
local map = {}
local blacklist = false
if not selector then return nil, false end
if type( selector ) == 'number' then
if selector < 0 then
selector = -selector
blacklist = true
end
map = { [ selector ] = true }
elseif type( selector ) == 'string' then
if string.sub( selector, 1, 1 ) == '-' then
selector = string.sub( selector, 2 )
blacklist = true
end
local ranges = mw.text.split( selector, ',' ) -- split ranges: '1,3-5' to {'1','3-5'}
for _, range in pairs( ranges ) do
range = mw.text.trim( range )
local min, max = mw.ustring.match( range, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( range, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do map[ i ] = true end
else
map[ range ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
-- List has the form { [1] = false, [2] = true, ['c'] = false }
-- Convert it to { [1] = true, [2] = true, ['c'] = true }
-- But if ANY value is set to false, treat the list as a blacklist
elseif type( selector ) == 'table' then
for i, v in pairs( selector ) do
if v == false then blacklist = true end
map[ i ] = true
end
end
if not blacklist and ( not map or map[ value ] )
or blacklist and map and not map[ value ] then
return true
end
end
-- Get the requested tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tags to return, for example 'div' or 'div,span,gallery'. Omit to return all tags.
-- @return Sequence of strings containing the wikitext of the requested tags.
-- @return Original wikitext minus requested tags.
function WikitextParser.getTags( wikitext, selector )
local tags = {}
local tagName, tagText, tagEnd
local original = wikitext
local count = 0
for tagStart, tagOpen in string.gmatch( original, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tagText = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len(tagOpen) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( original, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tagText = string.sub( original, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tagText = string.sub( original, tagStart, tagEnd )
end
count = count + 1
if inSelector( selector, tagName ) then
table.insert( tags, tagText )
else
wikitext = removeString( wikitext, tagText )
end
end
return tags, wikitext
end
return WikitextParser
0jpqzjirvxhndot81bm2g7c4xhdygv0
797370
797369
2024-02-22T14:52:52Z
en>Sophivorus
0
Merge parseSelector and isSelected into inSelector and generalize
797370
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper function to remove a string from a text
local function removeString( text, str )
local pattern = escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. escapeString( mw.ustring.sub( str, -999 ) )
end
return string.gsub( text, pattern, '' )
end
-- Helper function to determine if the given value is in the given selector or not
-- @param value Value to test, for example 'div' or 1
-- @param selector Comma-separated list of values, for example 'div,span' or '1,3-5'
-- @return boolean
local function inSelector( value, selector )
local map = {}
local blacklist = false
if not selector then return true end
if type( selector ) == 'number' then
if selector < 0 then
selector = -selector
blacklist = true
end
map = { [ selector ] = true }
elseif type( selector ) == 'string' then
if string.sub( selector, 1, 1 ) == '-' then
selector = string.sub( selector, 2 )
blacklist = true
end
local ranges = mw.text.split( selector, ',' ) -- split ranges: '1,3-5' to {'1','3-5'}
for _, range in pairs( ranges ) do
range = mw.text.trim( range )
local min, max = mw.ustring.match( range, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( range, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do map[ i ] = true end
else
map[ range ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
-- List has the form { [1] = false, [2] = true, ['c'] = false }
-- Convert it to { [1] = true, [2] = true, ['c'] = true }
-- But if ANY value is set to false, treat the list as a blacklist
elseif type( selector ) == 'table' then
for i, v in pairs( selector ) do
if v == false then blacklist = true end
map[ i ] = true
end
end
if not blacklist and ( not map or map[ value ] )
or blacklist and map and not map[ value ] then
return true
end
end
-- Get the requested tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tags to return, for example 'div' or 'div,span,gallery'. Omit to return all tags.
-- @return Sequence of strings containing the wikitext of the requested tags.
-- @return Original wikitext minus requested tags.
function WikitextParser.getTags( wikitext, selector )
local tags = {}
local tagName, tagText, tagEnd
local original = wikitext
local count = 0
for tagStart, tagOpen in string.gmatch( original, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tagText = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len(tagOpen) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( original, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tagText = string.sub( original, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tagText = string.sub( original, tagStart, tagEnd )
end
count = count + 1
if inSelector( tagName, selector ) then
table.insert( tags, tagText )
else
wikitext = removeString( wikitext, tagText )
end
end
return tags, wikitext
end
return WikitextParser
onoc6on1ktiw3uhac0nuf2zu4b14m3v
797371
797370
2024-02-22T15:02:10Z
en>Sophivorus
0
Bring and adapt getTables from [[Module:Transcluder]]
797371
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper function to remove a string from a text
local function removeString( text, str )
local pattern = escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. escapeString( mw.ustring.sub( str, -999 ) )
end
return string.gsub( text, pattern, '' )
end
-- Helper function to determine if the given value is filtered by the given selector
-- @todo Simplify and generalize even further
-- @param value Value to test, for example 'div' or 1
-- @param selector Comma-separated list of values, for example 'div,span' or '1,3-5'
-- @return boolean
local function inSelector( value, selector )
local map = {}
local blacklist = false
if not selector then return true end
if type( selector ) == 'number' then
if selector < 0 then
selector = -selector
blacklist = true
end
map = { [ selector ] = true }
elseif type( selector ) == 'string' then
if string.sub( selector, 1, 1 ) == '-' then
selector = string.sub( selector, 2 )
blacklist = true
end
local ranges = mw.text.split( selector, ',' ) -- split ranges: '1,3-5' to {'1','3-5'}
for _, range in pairs( ranges ) do
range = mw.text.trim( range )
local min, max = mw.ustring.match( range, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( range, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do map[ i ] = true end
else
map[ range ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
-- List has the form { [1] = false, [2] = true, ['c'] = false }
-- Convert it to { [1] = true, [2] = true, ['c'] = true }
-- But if ANY value is set to false, treat the list as a blacklist
elseif type( selector ) == 'table' then
for i, v in pairs( selector ) do
if v == false then blacklist = true end
map[ i ] = true
end
end
if not blacklist and ( not map or map[ value ] )
or blacklist and map and not map[ value ] then
return true
end
end
-- Get the requested tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tags to return, for example 'div' or 'div,span,gallery'. Omit to return all tags.
-- @return Sequence of strings containing the wikitext of the requested tags.
-- @return Original wikitext minus requested tags.
function WikitextParser.getTags( wikitext, selector )
local tags = {}
local tagName, tagText, tagEnd
local original = wikitext
local count = 0
for tagStart, tagOpen in string.gmatch( original, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tagText = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len(tagOpen) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( original, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tagText = string.sub( original, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tagText = string.sub( original, tagStart, tagEnd )
end
count = count + 1
if inSelector( tagName, selector ) then
table.insert( tags, tagText )
else
wikitext = removeString( wikitext, tagText )
end
end
return tags, wikitext
end
-- Get the requested tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tables to return, for example 2 or '1,3-5'. Omit to return all tables.
-- @return Sequence of strings containing the wikitext of the requested tables.
-- @return Original wikitext minus requested tables.
local function getTables( wikitext, selector )
local tables = {}
local id
local count = 0
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
id = string.match( t, '\n{|[^\n]-id%s*=%s*["\']?([^"\'\n]+)["\']?[^\n]*\n' )
count = count + 1
if inSelector( id, selector ) or inSelector( count, selector ) then
table.insert( tables, t )
else
wikitext = removeString( wikitext, t )
end
end
end
return tables, wikitext
end
return WikitextParser
8zdatyuwqfu2jfup4suc9p1apbfr6cl
797372
797371
2024-02-22T15:10:24Z
en>Sophivorus
0
Expose getTables
797372
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Helper function to remove a string from a text
local function removeString( text, str )
local pattern = escapeString( str )
if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
pattern = escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. escapeString( mw.ustring.sub( str, -999 ) )
end
return string.gsub( text, pattern, '' )
end
-- Helper function to determine if the given value is filtered by the given selector
-- @todo Simplify and generalize even further
-- @param value Value to test, for example 'div' or 1
-- @param selector Comma-separated list of values, for example 'div,span' or '1,3-5'
-- @return boolean
local function inSelector( value, selector )
local map = {}
local blacklist = false
if not selector then return true end
if type( selector ) == 'number' then
if selector < 0 then
selector = -selector
blacklist = true
end
map = { [ selector ] = true }
elseif type( selector ) == 'string' then
if string.sub( selector, 1, 1 ) == '-' then
selector = string.sub( selector, 2 )
blacklist = true
end
local ranges = mw.text.split( selector, ',' ) -- split ranges: '1,3-5' to {'1','3-5'}
for _, range in pairs( ranges ) do
range = mw.text.trim( range )
local min, max = mw.ustring.match( range, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5
if not max then min, max = string.match( range, '^((%d+))$' ) end -- '1' to min=1 max=1
if max then
for i = min, max do map[ i ] = true end
else
map[ range ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
end
end
-- List has the form { [1] = false, [2] = true, ['c'] = false }
-- Convert it to { [1] = true, [2] = true, ['c'] = true }
-- But if ANY value is set to false, treat the list as a blacklist
elseif type( selector ) == 'table' then
for i, v in pairs( selector ) do
if v == false then blacklist = true end
map[ i ] = true
end
end
if not blacklist and ( not map or map[ value ] )
or blacklist and map and not map[ value ] then
return true
end
end
-- Get the requested tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tags to return, for example 'div' or 'div,span,gallery'. Omit to return all tags.
-- @return Sequence of strings containing the wikitext of the requested tags.
-- @return Original wikitext minus requested tags.
function WikitextParser.getTags( wikitext, selector )
local tags = {}
local tagName, tagText, tagEnd
local original = wikitext
local count = 0
for tagStart, tagOpen in string.gmatch( original, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tagText = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len(tagOpen) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( original, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tagText = string.sub( original, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( original, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tagText = string.sub( original, tagStart, tagEnd )
end
count = count + 1
if inSelector( tagName, selector ) then
table.insert( tags, tagText )
else
wikitext = removeString( wikitext, tagText )
end
end
return tags, wikitext
end
-- Get the requested tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param selector Tables to return, for example 2 or '1,3-5'. Omit to return all tables.
-- @return Sequence of strings containing the wikitext of the requested tables.
-- @return Original wikitext minus requested tables.
function WikitextParser.getTables( wikitext, selector )
local tables = {}
local id
local count = 0
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
id = string.match( t, '\n{|[^\n]-id%s*=%s*["\']?([^"\'\n]+)["\']?[^\n]*\n' )
count = count + 1
if inSelector( id, selector ) or inSelector( count, selector ) then
table.insert( tables, t )
else
wikitext = removeString( wikitext, t )
end
end
end
return tables, wikitext
end
return WikitextParser
6u4sw52pmvjygde4npacmnr60nrn8hn
797373
797372
2024-02-27T13:27:54Z
en>Sophivorus
0
Testing simpler idea
797373
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
local function getNamespaces( name )
local namespaces = mw.clone( mw.site.namespaces[ name ].aliases ) -- Clone because of https://en.wikipedia.org/w/index.php?diff=1056921358
table.insert( namespaces, mw.site.namespaces[ name ].name )
table.insert( namespaces, mw.site.namespaces[ name ].canonicalName )
return namespaces
end
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tagName, tagText, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tagText = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tagText = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tagText = string.sub( wikitext, tagStart, tagEnd )
end
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested links.
local function getLinks( wikitext )
local links = {}
for link in string.gmatch( text, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
local function getCategories( wikitext )
local categories = {}
local namespace
local namespaces = WikitextParser.getNamespaces( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if namespaces[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
l65x4u2ebf5190qassmev63ohntrtqd
797374
797373
2024-02-27T13:39:17Z
en>Sophivorus
0
Fix getTags
797374
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
local function getNamespaces( name )
local namespaces = mw.clone( mw.site.namespaces[ name ].aliases ) -- Clone because of https://en.wikipedia.org/w/index.php?diff=1056921358
table.insert( namespaces, mw.site.namespaces[ name ].name )
table.insert( namespaces, mw.site.namespaces[ name ].canonicalName )
return namespaces
end
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested links.
local function getLinks( wikitext )
local links = {}
for link in string.gmatch( text, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
local function getCategories( wikitext )
local categories = {}
local namespace
local namespaces = WikitextParser.getNamespaces( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if namespaces[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
4n9o49wloz4o9c850r8wqsc9whgeq5d
797375
797374
2024-02-27T13:42:05Z
en>Sophivorus
0
797375
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested links.
local function getLinks( wikitext )
local links = {}
for link in string.gmatch( text, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
local function getCategories( wikitext )
local categories = {}
local namespace
local namespaces = WikitextParser.getNamespaces( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if namespaces[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
local function getNamespaces( name )
local namespaces = mw.clone( mw.site.namespaces[ name ].aliases ) -- Clone because of https://en.wikipedia.org/w/index.php?diff=1056921358
table.insert( namespaces, mw.site.namespaces[ name ].name )
table.insert( namespaces, mw.site.namespaces[ name ].canonicalName )
return namespaces
end
return WikitextParser
1v1nnmz67xxucvs6abv0sla2qm2da9q
797376
797375
2024-02-27T13:45:44Z
en>Sophivorus
0
Add getFiles
797376
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
local function getLinks( wikitext )
local links = {}
for link in string.gmatch( text, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
local function getFiles( wikitext )
local files = {}
local namespace
local namespaces = WikitextParser.getNamespaces( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if namespaces[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
local function getCategories( wikitext )
local categories = {}
local namespace
local namespaces = WikitextParser.getNamespaces( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if namespaces[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
local function getNamespaces( name )
local namespaces = mw.clone( mw.site.namespaces[ name ].aliases ) -- Clone because of https://en.wikipedia.org/w/index.php?diff=1056921358
table.insert( namespaces, mw.site.namespaces[ name ].name )
table.insert( namespaces, mw.site.namespaces[ name ].canonicalName )
return namespaces
end
return WikitextParser
68traopq4p2dsinncjrxwqn6fjh9l42
797377
797376
2024-02-27T13:54:02Z
en>Sophivorus
0
797377
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( text, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local namespace
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local namespace
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
local function getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
49r12hup81dda8mihqoaegb2soyfk21
797378
797377
2024-02-27T13:54:28Z
en>Sophivorus
0
797378
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local namespace
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local namespace
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
local function getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
rb5nyptdka34qp6bb0imv4ihxcl0ip0
797379
797378
2024-02-27T14:01:16Z
en>Sophivorus
0
797379
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t )
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
rn860otdlr8bq28qwm43q8fxwdztc4q
797380
797379
2024-02-27T14:02:19Z
en>Sophivorus
0
797380
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
jtf2kla3pbzuc0eaqozxougdgfvyr1t
797381
797380
2024-02-27T14:03:43Z
en>Sophivorus
0
Refine getTables
797381
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( wikitext, '^%b{}' ) do
if string.sub( t, 1, 2 ) == '{|' then
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
mgg0a3bexpq0ftst6h4aofuqtoa7mkp
797382
797381
2024-02-27T14:05:50Z
en>Sophivorus
0
Refine getTables
797382
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
edms9qqh6dsq53ycxtz7artc7paiylc
797383
797382
2024-02-27T14:13:48Z
en>Sophivorus
0
797383
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if namespace and aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?.+ ?:.+%]%]' )
if namespace and aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
dm6t4rjxlfof7r6mk17frjn565f5ck8
797384
797383
2024-02-27T14:16:08Z
en>Sophivorus
0
797384
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
o2ou4n7eik2ssbmunl8uf9v8yaaumfu
797385
797384
2024-02-27T14:17:53Z
en>Sophivorus
0
797385
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
9vz59jnzlgxab86e8q75ap9br1p5zcf
797386
797385
2024-02-27T14:18:40Z
en>Sophivorus
0
797386
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and aliases[ namespace ] then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
8dbaop17fkobwsj94kczjuhss0j9a5n
797387
797386
2024-02-27T14:19:59Z
en>Sophivorus
0
797387
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to get the local name of a namespace and all its aliases
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
function WikitextParser.getNamespaceAliases( name )
local aliases = mw.site.namespaces[ name ].aliases
table.insert( aliases, mw.site.namespaces[ name ].name )
table.insert( aliases, mw.site.namespaces[ name ].canonicalName )
return aliases
end
return WikitextParser
o5llr9bq6ymf8xevnnurdv51zruk30e
797388
797387
2024-02-27T14:34:48Z
en>Sophivorus
0
797388
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local aliases = WikitextParser.getNamespaceAliases( 'Category' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
behf8pnneq4797xt6zfow3s4pudmj85
797389
797388
2024-02-27T14:35:09Z
en>Sophivorus
0
797389
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
qawdqgoxhlefmymxyi6570iyoxhfhd1
797390
797389
2024-02-27T14:35:29Z
en>Sophivorus
0
797390
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if aliases[ namespace ] then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
7ohuiq9v31qplekdit2nir1tu6uimej
797391
797390
2024-02-27T14:36:35Z
en>Sophivorus
0
Fix canonical namespace check
797391
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local aliases = WikitextParser.getNamespaceAliases( 'File' )
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
n5c0eisdlzkykr8v1tgbe65lkm13vwh
797392
797391
2024-02-27T14:38:50Z
en>Sophivorus
0
797392
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" /> or <br/> or <hr>
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of strings containing the wikitext of the requested category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
ksfkcjczrmn02phoxnhck024098qz2f
797393
797392
2024-02-27T14:52:52Z
en>Sophivorus
0
Add getGalleries
797393
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
4aqdib0537i7ck7sr3zs9aappkcvd8r
797394
797393
2024-02-27T14:55:42Z
en>Sophivorus
0
Add getReferences
797394
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Author: User:Sophivorus
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all galleries from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of galleries.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get all references from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of references.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
o4lhojiuge5sesqj0aq334p2rem2bly
797395
797394
2024-02-27T15:16:28Z
en>Sophivorus
0
Add getTemplates
797395
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
local function getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get all ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
mkzjr364qezwahshae5c0mqky2rxaip
797396
797395
2024-02-27T15:24:43Z
en>Sophivorus
0
Expose getTemplates
797396
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get all templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get all tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get all gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get all ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get all tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get all internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get all file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get all category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
hd6sxboy10kimrco0t6dwlul030qtpx
797397
797396
2024-02-27T15:41:48Z
en>Sophivorus
0
Add getParameters
797397
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
bva2fpgodjlrr7ymhoovejzc2lq44p9
797398
797397
2024-02-27T15:48:58Z
en>Sophivorus
0
Add getTemplate
797398
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the first occurrence of the requested template in the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
for _, template in templates do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if templateName == name then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
twgw9jrvm3ilw3qe8vob2ikzggy6yp0
797399
797398
2024-02-27T15:50:17Z
en>Sophivorus
0
797399
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the first occurrence of the requested template in the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if templateName == name then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
rcbcd5vjl2lho2rvv9e3pai2ss6dlxo
797400
797399
2024-02-27T15:53:37Z
en>Sophivorus
0
797400
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the first occurrence of the requested template in the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
qq9v8yhtfw7wsuo3izh091qyndexyh2
797401
797400
2024-02-27T15:54:48Z
en>Sophivorus
0
797401
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the first occurrence of the requested template in the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
3rz9bwono4h1sk26w9ph91b6spf1j86
797402
797401
2024-02-27T15:59:55Z
en>Sophivorus
0
797402
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the first occurrence of a certain template in the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local parts, name, value, count
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
bwl923hzauh3542pt61trhaoifjlnx5
797403
797402
2024-02-27T16:02:17Z
en>Sophivorus
0
797403
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the first occurrence of a certain template in the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the gallery tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the ref tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
l7k0esx8xx4tsfy88dae96t7lzd4hgv
797404
797403
2024-02-29T12:01:17Z
en>Sophivorus
0
Add getSection, getLead and getSectionTag
797404
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
local function getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
local function getLead( wikitext )
wikitext = string.match( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the content of a <section> tag from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
local function getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
gzjrago5rpsjlm2ke67d8ie5b0rebkd
797405
797404
2024-02-29T12:08:50Z
en>Sophivorus
0
797405
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.match( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the content of a <section> tag from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
return WikitextParser
h60k9pcxa9i6btre1gsdy0c5u58wx33
797406
797405
2024-02-29T12:10:38Z
en>Sophivorus
0
797406
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.match( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the content of a <section> tag from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
gphs75cpm8tuwc878jbo9apz68lhntg
797407
797406
2024-02-29T12:14:47Z
en>Sophivorus
0
797407
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the content of a <section> tag from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
8fpsgqclji06iucmzbofprco27dtxht
797408
797407
2024-02-29T12:18:26Z
en>Sophivorus
0
797408
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the content of a <section> tag from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
1tuuon01nkvvgabw6kka9yn85dt2hyk
797409
797408
2024-02-29T12:25:31Z
en>Sophivorus
0
797409
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
quhkjzuvgatwijg5y69s5y3notvgivm
797410
797409
2024-02-29T12:31:18Z
en>Sophivorus
0
Add getLists
797410
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
local function getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
r3mtnctrq1hji99rp3x7jp5i8cqpjjz
797411
797410
2024-02-29T12:35:55Z
en>Sophivorus
0
797411
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
532x921zuxbdu7pu10z0xys9q48yg2x
797412
797411
2024-02-29T12:58:26Z
en>Sophivorus
0
Add getParagraphs
797412
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
local function getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
if paragraph:gmatch( '^[*#]' ) then
break; -- just a list
end
if paragraph:gmatch( '^{|.*|}$' ) then
break; -- just a table
end
if paragraph:gmatch( '^{{.*}}$' ) then
break; -- probably just a bunch of templates
end
if paragraph:gmatch( '^%[%b[]%]$' ) then
break; -- probably just a file link
end
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
9wgcpe8ndeumpcql0xyazv517vb0fmx
797413
797412
2024-02-29T13:03:54Z
en>Sophivorus
0
797413
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
local function getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
if paragraph:gmatch( '^=.*=$' ) then
break; -- just a section title
end
if paragraph:gmatch( '^[*#]' ) then
break; -- just a list
end
if paragraph:gmatch( '^{|.*|}$' ) then
break; -- just a table
end
if paragraph:gmatch( '^{{.*}}$' ) then
break; -- probably just a bunch of templates
end
if paragraph:gmatch( '^%[%b[]%]$' ) then
break; -- probably just a file link
end
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
a4heuurngyjlqtbr2ck7h5ywszzbvmb
797414
797413
2024-02-29T13:04:29Z
en>Sophivorus
0
797414
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
if paragraph:gmatch( '^=.*=$' ) then
break; -- just a section title
end
if paragraph:gmatch( '^[*#]' ) then
break; -- just a list
end
if paragraph:gmatch( '^{|.*|}$' ) then
break; -- just a table
end
if paragraph:gmatch( '^{{.*}}$' ) then
break; -- probably just a bunch of templates
end
if paragraph:gmatch( '^%[%b[]%]$' ) then
break; -- probably just a file link
end
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
fijkcd7n5nmm4ajgdayyzhzwuqdq0mu
797415
797414
2024-02-29T13:08:06Z
en>Sophivorus
0
797415
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
if paragraph:gmatch( '^[*#]' ) then
break; -- just a list
end
if paragraph:gmatch( '^{|.*|}$' ) then
break; -- just a table
end
if paragraph:gmatch( '^{{.*}}$' ) then
break; -- probably just a bunch of templates
end
if paragraph:gmatch( '^%[%b[]%]$' ) then
break; -- probably just a file link
end
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
4i33xcx72x3w5qzmc7w3ogx17jyaqbz
797416
797415
2024-02-29T13:08:19Z
en>Sophivorus
0
Add getParagraphs
797416
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
rh6c5ep665qvf3qofwc67e7k561pg3a
797417
797416
2024-02-29T13:08:43Z
en>Sophivorus
0
797417
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
if paragraph:gmatch( '^=.*=$' ) then
break; -- just a section title
end
if paragraph:gmatch( '^{|.*|}$' ) then
break; -- just a table
end
if paragraph:gmatch( '^{{.*}}$' ) then
break; -- probably just a bunch of templates
end
if paragraph:gmatch( '^%[%b[]%]$' ) then
break; -- probably just a file link
end
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
hluqhoyj6leamdl8h00ygtgzsx1102s
797418
797417
2024-02-29T13:08:56Z
en>Sophivorus
0
Add getParagraphs
797418
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
if paragraph:gmatch( '^=.*=$' ) then
break; -- just a section title
end
if paragraph:gmatch( '^[*#]' ) then
break; -- just a list
end
if paragraph:gmatch( '^{{.*}}$' ) then
break; -- probably just a bunch of templates
end
if paragraph:gmatch( '^%[%b[]%]$' ) then
break; -- probably just a file link
end
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
cq9vk771xqlnz8e5eirf9tc2hmqmdww
797419
797418
2024-02-29T13:09:16Z
en>Sophivorus
0
797419
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
if paragraph:gmatch( '^=.*=$' ) then
break; -- just a section title
end
if paragraph:gmatch( '^[*#]' ) then
break; -- just a list
end
if paragraph:gmatch( '^{|.*|}$' ) then
break; -- just a table
end
if paragraph:gmatch( '^%[%b[]%]$' ) then
break; -- probably just a file link
end
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
juegugpm49lnrjdwjspogd2u8cnexmk
797420
797419
2024-02-29T13:09:38Z
en>Sophivorus
0
797420
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
repeat
paragraph = mw.text.trim( paragraph )
if paragraph == '' then
break -- paragraph is empty
end;
if paragraph:gmatch( '^=.*=$' ) then
break; -- just a section title
end
if paragraph:gmatch( '^[*#]' ) then
break; -- just a list
end
if paragraph:gmatch( '^{|.*|}$' ) then
break; -- just a table
end
if paragraph:gmatch( '^{{.*}}$' ) then
break; -- probably just a bunch of templates
end
table.insert( paragraphs, paragraph )
until true
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
h8zqgpqxl7junxwxvtk35lyje8yp6i6
797421
797420
2024-02-29T13:28:38Z
en>Sophivorus
0
Rewrite getParagraphs
797421
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get a section from the given wikitext.
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections with headings at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param text Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param text Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param text Required. Template wikitext to parse.
-- @return Map from parameter name to value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
gv1uy9q7rpft74dkk88gcjnq00a64uv
797422
797421
2024-02-29T13:52:43Z
en>Sophivorus
0
Add getSections
797422
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
local function getSections( wikitext )
local sections = {}
for title in string.gmatch( '\n' .. wikitext .. '\n==', '\n==+ *([^=]+) *==+' ) do
local section = string.match( '\n' .. wikitext .. '\n==', '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
pgysc6xrjohnos2dt7s4yh0vwk9tjmz
797423
797422
2024-02-29T13:54:18Z
en>Sophivorus
0
797423
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = string.gsub( '\n' .. wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
for title in string.gmatch( '\n' .. wikitext .. '\n==', '\n==+ *([^=]+) *==+' ) do
local section = string.match( '\n' .. wikitext .. '\n==', '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
mk08o0qlbjs2ycnvktvnpsy4w5gkgus
797424
797423
2024-02-29T14:02:39Z
en>Sophivorus
0
797424
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @todo Reuse existing methods to make this method more robust
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
msgwbkh6nchcg2gpqr4zy7ye1hl5txh
797425
797424
2024-02-29T14:05:45Z
en>Sophivorus
0
797425
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
return WikitextParser
m0vmo2whujc18qi5nepwiz5e8isduvc
797426
797425
2024-02-29T14:11:25Z
en>Sophivorus
0
Add getExternalLinks
797426
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[(https://|http://|//)' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
k92l5e9nknstod60ov5komm6w5nw1cm
797427
797426
2024-02-29T14:16:30Z
en>Sophivorus
0
797427
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
qn2k0jr9cr2cn3onlb068ucjzabqatb
797428
797427
2024-02-29T14:16:58Z
en>Sophivorus
0
797428
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
if string.find( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>' ) then -- avoid expensive search if possible
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text between section tags
wikitext = wikitext:gsub( '^.-< *section +begin *= *["\']? *' .. name .. ' *["\']? */>', '') -- remove text before first section tag
wikitext = wikitext:gsub( '< *section +end= *["\']? *'.. name ..' *["\']? */>.*', '') -- remove text after last section tag
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
fr4y8agl65bqq32scw4sotu99d9m2ds
797429
797428
2024-02-29T14:28:37Z
en>Sophivorus
0
Simplify getSectionTag
797429
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags for this one because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
1qalh2f046poonmmn8ufdshcy87zzgg
797430
797429
2024-02-29T14:29:22Z
en>Sophivorus
0
797430
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
m2dibridyfku8lfzmglhjuxxnm807uz
797431
797430
2024-02-29T15:55:42Z
en>Sophivorus
0
Add checks
797431
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
local level, wikitext = string.match( '\n' .. wikitext .. '\n', '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
for list in string.gmatch( '\n' .. wikitext .. '\n\n', '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = string.match( template, '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = string.match( template, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '[%b[]]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = string.gsub( string.gsub( value, '@@:@@', '|' ), '@@_@@', '=' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/].->)' ) do
tagName = string.match( tagOpen, '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + string.len( tagOpen ) - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = string.sub( wikitext, tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = string.match( tag, '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
for t in string.gmatch( '\n' .. wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
gqtbr009ptt80uesk2lhcr57pzfdkbq
797432
797431
2024-03-03T13:00:37Z
en>Sophivorus
0
Unify code style
797432
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
60r7tpyrvd4oczhvpstz4108p3y58ik
797433
797432
2024-04-29T11:36:10Z
en>Sophivorus
0
Add methods getTableId and getTableById
797433
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( tableWikitext )
return string.match( tableWikitext, '{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, tableId )
local tables = WikitextParser.getTables( wikitext )
local id
for index, tableWikitext in ipairs( tables ) do
id = WikitextParser.getTableId( tableWikitext )
if id == tableId then
return tableWikitext
end
end
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
gvg9vd3zcmmbaoh6nwaduyco2qis243
797434
797433
2024-04-29T11:54:10Z
en>Sophivorus
0
Add methods getTableId and getTableById
797434
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( mw.text.trim( t ), '{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
gbiol5om6m7doff5khojm2iqinbo0cq
797435
797434
2024-04-29T11:54:45Z
en>Sophivorus
0
Add methods getTableId and getTableById
797435
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
l0d3sdzft3vasd1fbouc3nax03yyagr
797436
797435
2024-04-29T12:10:54Z
en>Sophivorus
0
Add first version of getTableData
797436
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^{|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
s1vuvok7su5lnwiscausp70krle7n0d
797437
797436
2024-04-29T12:11:53Z
en>Sophivorus
0
Add first version of getTableData
797437
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^{|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
rn4f84lvh6ankpxk61v4ic1kr66g128
797438
797437
2024-04-29T12:14:40Z
en>Sophivorus
0
797438
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = string.gsub( tableWikitext, '{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '{|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
6j579gpgmf9m3tlqukilpqoxmz9h7gb
797439
797438
2024-04-29T12:15:58Z
en>Sophivorus
0
797439
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^{|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|%-', true ) do
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
i4wxgj00nd3m5ikib8exwgx21ly3a68
797440
797439
2024-04-29T12:16:18Z
en>Sophivorus
0
797440
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^{|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
cupxls1ero3v4yrzovuj84o7mrg7p6z
797441
797440
2024-04-29T12:16:45Z
en>Sophivorus
0
797441
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
t55makz1jlkzvtipwqzjwnetgonp1la
797442
797441
2024-04-29T12:17:31Z
en>Sophivorus
0
797442
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
bl8hu0xyf014nb69pabaxc6gcmziwjv
797443
797442
2024-04-29T12:17:55Z
en>Sophivorus
0
797443
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
4e3rjs95f6kiymvu5xwxyg8ssc1o8d0
797444
797443
2024-04-29T12:18:22Z
en>Sophivorus
0
797444
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
t55makz1jlkzvtipwqzjwnetgonp1la
797445
797444
2024-04-29T12:19:41Z
en>Sophivorus
0
797445
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
4e3rjs95f6kiymvu5xwxyg8ssc1o8d0
797446
797445
2024-04-29T12:20:27Z
en>Sophivorus
0
797446
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
rowData = mw.clone( rowData );
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
ffv9ifkv069u1tfzluavq56uvhjb4ip
797447
797446
2024-04-29T12:20:45Z
en>Sophivorus
0
797447
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = mw.clone( rowData );
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
i4hirfc2p1b4363ledgfb805qteegdb
797448
797447
2024-04-29T12:21:07Z
en>Sophivorus
0
797448
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
local rowData = {}
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local data = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( data, cellWikitext )
end
table.insert( tableData, data )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
nk87yhf5vq5wuftpwc7mx8mnaptfvt0
797449
797448
2024-04-29T12:22:26Z
en>Sophivorus
0
797449
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove the caption
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
bl8hu0xyf014nb69pabaxc6gcmziwjv
797450
797449
2024-04-29T12:25:04Z
en>Sophivorus
0
797450
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
ocl8ft0sh1nloeezfpdmei393130227
797451
797450
2024-04-29T12:25:39Z
en>Sophivorus
0
797451
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableWikitext
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
cqci8mv9zuyjfw0ark4rt21immvjjm0
797452
797451
2024-04-29T12:26:28Z
en>Sophivorus
0
797452
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableWikitext
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
i875i16tbjlc6hnyuwsk9078ngqe48y
797453
797452
2024-04-29T12:26:40Z
en>Sophivorus
0
797453
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '||' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
5ppgjrx5o5u0ipgykafm09fdun6fgwa
797454
797453
2024-04-29T12:27:14Z
en>Sophivorus
0
797454
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
for cellWikitext in mw.text.gsplit( rowWikitext, '[|!][|!]' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
qiw0yvtbdrvgcr1ob2zdnwgms68p332
797455
797454
2024-04-29T12:30:27Z
en>Sophivorus
0
797455
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' ) -- convert any || to \n|
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' ) -- convert any !! to \n|
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' ) -- convert any \n! to \n|
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
s24o3xqheqcqzzp3zpwecb76xjtzrcy
797456
797455
2024-04-29T12:30:57Z
en>Sophivorus
0
797456
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = mw.text.trim( rowWikitext );
rowWikitext = string.gsub( rowWikitext, '||', '\n|' ) -- convert any || to \n|
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' ) -- convert any !! to \n|
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' ) -- convert any \n! to \n|
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
f9s905i1irkthqwpluk9s8k7du3cfiq
797457
797456
2024-04-29T12:32:05Z
en>Sophivorus
0
797457
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' ) -- convert any || to \n|
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' ) -- convert any !! to \n|
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' ) -- convert any \n! to \n|
rowWikitext = string.gsub( rowWikitext, '^\n|', '' ) -- remove the leading \n|
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9vnr4gla8i8ck3a0syscmtiu5rcxjnw
797458
797457
2024-04-29T12:32:54Z
en>Sophivorus
0
797458
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' ) -- convert any || to \n|
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' ) -- convert any !! to \n|
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' ) -- convert any \n! to \n|
rowWikitext = string.gsub( rowWikitext, '^|', '' ) -- remove the leading |
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9olrndqm2yuzkn2d27al93kzi5av1yv
797459
797458
2024-04-29T12:34:00Z
en>Sophivorus
0
797459
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
nrkb32vcni659sb7acyxgdx5a55h6jn
797460
797459
2024-04-29T12:34:32Z
en>Sophivorus
0
797460
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
lblu1oygetm8v0fhzv7c82c9hfiy704
797461
797460
2025-03-18T11:31:33Z
en>Sophivorus
0
Add methods to replace elements for placeholders and then restore them
797461
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for element in elements do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' + index + '@@@' )
replacedElements:insert( element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for match in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = match[0];
local index = tonumber( match[1] );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
replacedElements:remove( index );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in ipairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
d74nbm6cxee41sljc0gk186fajtnjuz
797462
797461
2025-03-18T11:39:22Z
en>Sophivorus
0
797462
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' + index + '@@@' )
replacedElements:insert( element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for match in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = match[0];
local index = tonumber( match[1] );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
replacedElements:remove( index );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
8nz5zabm3yjx66oqmhazvselplc8850
797463
797462
2025-03-18T11:39:47Z
en>Sophivorus
0
797463
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
replacedElements:insert( element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for match in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = match[0];
local index = tonumber( match[1] );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
replacedElements:remove( index );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
4h4zbc4wkjjm48vsfjxk5k34hnnor8l
797464
797463
2025-03-18T11:40:54Z
en>Sophivorus
0
797464
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( element, replacedElements )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for match in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = match[0];
local index = tonumber( match[1] );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
table.remove( index, replacedElements );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
5ze90pq9da50s86vbe6dw70pw9hupaa
797465
797464
2025-03-18T11:41:59Z
en>Sophivorus
0
797465
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for match in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = match[0];
local index = tonumber( match[1] );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
table.remove( replacedElements, index );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9sk0eu4v203xwmfvg76yd7fm0j3c6qc
797466
797465
2025-03-18T11:53:25Z
en>Sophivorus
0
797466
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for capture in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. capture[0] .. '@@@';
local index = tonumber( capture[0] );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
table.remove( replacedElements, index );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
l69jce98qv4icfw1iqu5kdw8rk5bjbw
797467
797466
2025-03-18T11:54:54Z
en>Sophivorus
0
797467
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
index = tonumber( index );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
table.remove( replacedElements, index );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
c1rew85imj26ai5d4a0qpcsdpooonpx
797468
797467
2025-03-18T11:55:16Z
en>Sophivorus
0
797468
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
table.remove( replacedElements, index );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
isdaugj8018250vz7c9bjd47ed6tgy8
797469
797468
2025-03-18T11:57:13Z
en>Sophivorus
0
797469
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
table.remove( replacedElements, index );
end
return index;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
ln0ocdh6bgrn7kdvzxz2rjh4c0i5a9f
797470
797469
2025-03-18T11:57:56Z
en>Sophivorus
0
797470
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
end
return index
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
8wl243tmvw4qzcsl7c5o2apyeqp017t
797471
797470
2025-03-18T11:58:17Z
en>Sophivorus
0
797471
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local index
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
end
return index
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
mhuww16pc5f2ncyrkvfb89b4azwrv4s
797472
797471
2025-03-18T11:58:31Z
en>Sophivorus
0
797472
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local index
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
end
return index
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
ezzojnfhnnk4fmaco8n96cs9kcqzc4j
797473
797472
2025-03-18T12:00:53Z
en>Sophivorus
0
797473
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index );
local element = replacedElements[ index ];
wikitext = wikitext:gsub( placeholder, element );
table.remove( replacedElements, index );
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
isdaugj8018250vz7c9bjd47ed6tgy8
797474
797473
2025-03-18T12:03:19Z
en>Sophivorus
0
797474
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local index
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
end
return index;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
0plp15b40ehduixeykiy0xz73o7qvyp
797475
797474
2025-03-18T12:05:08Z
en>Sophivorus
0
797475
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local index
for index in wikitext:gmatch( '@@@(%d-)@@@' ) do
end
return index;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
bihjz9xny0329h8byjkhslehzyaoosi
797476
797475
2025-03-18T12:07:02Z
en>Sophivorus
0
797476
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local index
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
index = 'j'
end
return index;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
mys008idlc29tprd20627r28ulw792w
797477
797476
2025-03-18T12:09:37Z
en>Sophivorus
0
797477
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local index
for index in string.gmatch( wikitext, '@@@(%d+)@@@' ) do
index = 'j'
end
return index;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9ojpwnqjtt157jry9cs3w08slal0auc
797478
797477
2025-03-18T12:10:57Z
en>Sophivorus
0
797478
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for placeholder in wikitext:gmatch( '@@@%d+@@@' ) do
end
return placeholder;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
rsdrf7e8bhmfste8wbp1fybewj7ltiv
797479
797478
2025-03-18T12:12:30Z
en>Sophivorus
0
797479
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for placeholder in wikitext:gmatch( '@@@%d+@@@' ) do
end
return wikitext;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
hljbclx9e8ath1htig7le25kmapefdo
797480
797479
2025-03-18T12:12:46Z
en>Sophivorus
0
797480
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for placeholder in wikitext:gmatch( '@@@%d+@@@' ) do
end
return wikitext:gmatch( '@@@%d+@@@' );
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
3gjtsyxvh70dwgyiunfj0hh4z7m99jm
797481
797480
2025-03-18T12:13:48Z
en>Sophivorus
0
797481
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@%d+@@@' ) do
placeholder = index
end
return placeholder;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
3ohhuo6iq5imk6yt4bfpos4a44vbjos
797482
797481
2025-03-18T12:14:04Z
en>Sophivorus
0
797482
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
placeholder = index
end
return placeholder;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
io3mcki5wqyi2306o08hap6k0rn9k04
797483
797482
2025-03-18T12:14:40Z
en>Sophivorus
0
797483
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
placeholder = tonumber( index )
end
return placeholder;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
0bu3obc0fbw1ljdxmnod3djjmkzykz3
797484
797483
2025-03-18T12:15:08Z
en>Sophivorus
0
797484
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
index = tonumber( index )
placeholder = replacedElements[ index ]
end
return placeholder;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
8qjr6puqkijwliaymok7c3rh4kx60fm
797485
797484
2025-03-18T12:15:32Z
en>Sophivorus
0
797485
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
index = tonumber( index )
placeholder = #replacedElements
end
return placeholder;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
3w3whd9vvl7orvx4kj4j2vtse3sxro7
797486
797485
2025-03-18T12:15:50Z
en>Sophivorus
0
797486
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
index = tonumber( index ) + 1
placeholder = replacedElements[ index ]
end
return placeholder;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
aovpw2jtomp3gbhk2zn17zdsjxerlya
797487
797486
2025-03-18T12:17:44Z
en>Sophivorus
0
797487
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
placeholder = '@@@' .. index .. '@@@'
index = tonumber( index ) + 1
end
return placeholder;
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
rngzmpmwf4yyf3azd3ix7juj0f07oz1
797488
797487
2025-03-18T12:18:37Z
en>Sophivorus
0
797488
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
placeholder = '@@@' .. index .. '@@@'
index = tonumber( index ) + 1
end
return mw.dumpObject( replacedElements );
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
130xg2pkvw9ofrl6j7e8ombsefnyq0b
797489
797488
2025-03-18T12:19:25Z
en>Sophivorus
0
797489
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local placeholder
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
placeholder = '@@@' .. index .. '@@@'
index = tonumber( index ) + 1
end
return mw.dumpObject( replacedElements[0] );
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
jb58nx4j43scj8eoqz70g6wm76hhunp
797490
797489
2025-03-18T12:23:12Z
en>Sophivorus
0
797490
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
element = replacedElements[ index ]
wikitext = wikitext:gsub( '@@@' .. index .. '@@@', element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9fi30uz2nbh21emr3eo370fpy852oqk
797491
797490
2025-03-18T12:23:29Z
en>Sophivorus
0
797491
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = replacedElements[ index ]
wikitext = wikitext:gsub( '@@@' .. index .. '@@@', element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
ph4szuvntg4lmmi714t0gyycqp2b8v1
797492
797491
2025-03-18T12:24:24Z
en>Sophivorus
0
797492
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = replacedElements[ index ]
wikitext = wikitext:gsub( placeholder, element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
trbs4fooy9jha25pq1keiz0fblh0yn7
797493
797492
2025-03-18T12:30:16Z
en>Sophivorus
0
797493
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
replacedElements[ index ] = element
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = table.remove( replacedElements, index )
wikitext = wikitext:gsub( placeholder, element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
bkyzpxhkw6usq6j98oyriuwyzghjwx2
797494
797493
2025-03-18T12:32:50Z
en>Sophivorus
0
797494
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = table.remove( replacedElements, index )
wikitext = wikitext:gsub( placeholder, element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
2qeookxt67qrqdk7zzjyf94lonkl3b4
797495
797494
2025-03-18T12:33:41Z
en>Sophivorus
0
797495
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
replacedElements[ index ] = element
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = table.remove( replacedElements, index )
--wikitext = wikitext:gsub( placeholder, element )
end
return mw.dumpObject( replacedElements )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
fn93kfzlscnepx5cds3h4uw27yj3n7m
797496
797495
2025-03-18T12:34:05Z
en>Sophivorus
0
797496
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
replacedElements[ index ] = element
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = table.remove( replacedElements, index )
--wikitext = wikitext:gsub( placeholder, element )
end
return mw.dumpObject( element )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
87hk08wlyxern1yph09pujzn3lg7cw3
797497
797496
2025-03-18T12:34:27Z
en>Sophivorus
0
797497
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
replacedElements[ index ] = element
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
local element
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
element = table.remove( replacedElements, index )
--wikitext = wikitext:gsub( placeholder, element )
end
return mw.dumpObject( element )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
ncto9jzf4bx8abvh3rjclbyhkhedf88
797498
797497
2025-03-18T12:35:52Z
en>Sophivorus
0
797498
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
replacedElements[ index ] = element
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = table.remove( replacedElements, index )
wikitext = wikitext:gsub( placeholder, element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
bkyzpxhkw6usq6j98oyriuwyzghjwx2
797499
797498
2025-03-18T12:36:47Z
en>Sophivorus
0
797499
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
replacedElements[ index ] = element
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = table.remove( replacedElements, index )
wikitext = element
--wikitext = wikitext:gsub( placeholder, element )
end
return mw.dumpObject( wikitext )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
a31hzm1xffi3ympfs089008earvgq0g
797500
797499
2025-03-18T12:37:55Z
en>Sophivorus
0
797500
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = replacedElements[ index ]
wikitext = wikitext:gsub( placeholder, element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9l5ecsjs7jt74rotv545ny1rk6oty73
797501
797500
2025-03-18T12:45:04Z
en>Sophivorus
0
797501
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
-- Replace the given elements in the given wikitext for placeholders
-- Placeholders have the form @@@1@@@, @@@2@@@, @@@3@@@, etc.
-- @param wikitext Required. Wikitext to parse.
-- @param elements Required. Elements to replace.
-- @return Wikitext with elements replaced for placeholders.
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
table.insert( replacedElements, element )
end
end
return wikitext;
end
-- Restore the elements in the given wikitext that were replaced by placeholders
-- @param wikitext Required. Wikitext with placeholders.
-- @return Wikitext with elements restored.
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = replacedElements[ index ]
wikitext = wikitext:gsub( placeholder, element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
gtjhoi4zxiny80kr3q4ipfjzmou5faz
797502
797501
2025-03-18T12:48:43Z
en>Sophivorus
0
797502
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
-- Replace the given elements in the given wikitext for placeholders of the form @@@1@@@, @@@2@@@, etc.
-- This method is used to replace nested elements that would otherwise complicate parsing enormously
-- @param wikitext Required. Wikitext to parse.
-- @param elements Required. Elements to replace.
-- @return Wikitext with elements replaced for placeholders.
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements
replacedElements[ index ] = element
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
end
end
return wikitext;
end
-- Restore the elements in the given wikitext that were replaced by placeholders
-- @param wikitext Required. Wikitext with placeholders.
-- @return Wikitext with elements restored.
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = replacedElements[ index ]
wikitext = wikitext:gsub( placeholder, element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
o3y4a3hfvoxajoi51tu53pwym3htln9
797503
797502
2025-03-18T12:50:51Z
en>Sophivorus
0
797503
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private variable to store the elements that were replaced by placeholders
local replacedElements = {}
-- Replace the given elements in the given wikitext for placeholders of the form @@@1@@@, @@@2@@@, etc.
-- This method is used to replace nested elements that would otherwise complicate parsing enormously
-- @param wikitext Required. Wikitext to parse.
-- @param elements Required. Elements to replace.
-- @return Wikitext with elements replaced for placeholders.
function WikitextParser.replaceElements( wikitext, elements )
for _, element in pairs( elements ) do
local escapedElement = escapeString( element )
if wikitext:match( escapedElement ) then
local index = #replacedElements + 1
replacedElements[ index ] = element
wikitext = wikitext:gsub( escapedElement, '@@@' .. index .. '@@@' )
end
end
return wikitext;
end
-- Restore the elements in the given wikitext that were replaced by placeholders
-- @param wikitext Required. Wikitext with placeholders.
-- @return Wikitext with elements restored.
function WikitextParser.restoreElements( wikitext )
for index in wikitext:gmatch( '@@@(%d+)@@@' ) do
local placeholder = '@@@' .. index .. '@@@'
index = tonumber( index )
local element = replacedElements[ index ]
wikitext = wikitext:gsub( placeholder, element )
end
return wikitext
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
sp50vy59kpdz7t7zvfzcypa9jig3txr
797504
797503
2025-03-18T13:09:45Z
en>Sophivorus
0
Replacing elements not currently needed
797504
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
5pujucucldzogrna1cn8rinoq4h349p
797505
797504
2025-03-18T14:27:20Z
en>Sophivorus
0
797505
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '%[%[ ?(.+) ?:.+%]%]' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
sqccnnsowst5daekyzb0dxywn9d8mlx
797506
797505
2025-03-18T14:28:39Z
en>Sophivorus
0
Refine namespace matching
797506
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = template:match( '^{{ *([^}|\n]+)' )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get the parameters from the given template.
-- @param wikitext Required. Template wikitext to parse.
-- @return Map from parameter name to parameter value
function WikitextParser.getParameters( template )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
end
end
return parameters
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
moh487lhunl38p3wes3oap2v80sgreu
797507
797506
2025-03-20T11:58:51Z
en>Sophivorus
0
Add getTemplateName
797507
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
53p06jik0ctedsub2gjtv2uy68exlsi
797508
797507
2025-03-20T12:01:47Z
en>Sophivorus
0
797508
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local params = template:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
jhmtny6xr75q3xyp0edioe8d0s75gd2
797509
797508
2025-03-20T12:08:05Z
en>Sophivorus
0
797509
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
hhnxaeda8a41wysyc75xk5qg3x8exj9
797510
797509
2025-03-20T12:09:04Z
en>Sophivorus
0
797510
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = mw.text.trim( table.concat( parts, '=', 2 ) )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
likkmla2zomnf0d0qc32zvunswmerns
797511
797510
2025-03-20T12:18:38Z
en>Sophivorus
0
797511
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
pxz5ye9ndf2wj4x3kpzvq1wq9rh2skb
797512
797511
2025-03-20T12:24:05Z
en>Sophivorus
0
797512
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return params,parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
glv1o55twir8uz34fdwz34o5pf21orv
797513
797512
2025-03-20T12:24:38Z
en>Sophivorus
0
797513
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
pxz5ye9ndf2wj4x3kpzvq1wq9rh2skb
797514
797513
2025-03-20T12:33:40Z
en>Sophivorus
0
797514
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '%', '%%' ):gsub( '|', '@@:@@' ):gsub( '=', '@@_@@' ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '%', '%%' ):gsub( '|', '@@:@@' ):gsub( '=', '@@_@@' ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9cl49o0bhim8x848jg3kdyvcfdspayk
797515
797514
2025-03-20T12:34:46Z
en>Sophivorus
0
797515
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '|', '@@:@@' ):gsub( '=', '@@_@@' ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '|', '@@:@@' ):gsub( '=', '@@_@@' ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
23i1yo0onvwfo3b29hncfkc96vnenb4
797516
797515
2025-03-20T12:35:44Z
en>Sophivorus
0
797516
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '%%', '%%' ):gsub( '|', '@@:@@' ):gsub( '=', '@@_@@' ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '%%', '%%' ):gsub( '|', '@@:@@' ):gsub( '=', '@@_@@' ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
jyauhkwwa31oyw7mclr21hcprqq3wga
797517
797516
2025-03-20T12:36:20Z
en>Sophivorus
0
797517
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
pxz5ye9ndf2wj4x3kpzvq1wq9rh2skb
797518
797517
2025-03-20T12:40:08Z
en>Sophivorus
0
797518
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
t5w1zb0ehh2d16swg0ha8hi46zidbvt
797519
797518
2025-03-20T12:40:53Z
en>Sophivorus
0
797519
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9477yo0o97oweveuidrbq4hapacro4t
797520
797519
2025-03-20T13:00:20Z
en>Sophivorus
0
797520
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '[%b[]]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
pxz5ye9ndf2wj4x3kpzvq1wq9rh2skb
797521
797520
2025-03-20T13:03:20Z
en>Sophivorus
0
797521
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
9477yo0o97oweveuidrbq4hapacro4t
797522
797521
2025-03-21T13:07:03Z
en>Sophivorus
0
Add getFileName
797522
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]+) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '%[%[[^:]-:([^]|]+)' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
oz4r8sowcg5nmbhrnkicyx3esnnd46g
797523
797522
2025-03-22T17:55:42Z
en>Sophivorus
0
797523
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Helper function to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '%[%[[^:]-:([^]|]+)' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
6e24c4lxa5qnqpb8hfqe54sit9v2pr4
797524
797523
2025-03-22T19:56:55Z
en>Sophivorus
0
Improve getFileName
797524
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Private helper method to get all the local names of a namespace (the canonical name and all its aliases)
-- @param name Canonical name of the namespace, for example 'File'
-- @return Local name of the namespace and all aliases, for example { 'File', 'Image', 'Archivo', 'Imagen' }
function Excerpt.getNamespaces( name )
local namespaces = mw.clone( mw.site.namespaces[ name ].aliases ) -- clone because https://en.wikipedia.org/w/index.php?diff=1056921358
table.insert( namespaces, mw.site.namespaces[ name ].name )
table.insert( namespaces, mw.site.namespaces[ name ].canonicalName )
return namespaces
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ -.-:([^]|]-)' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
1chctcgfnpgyli2inoqijzy17k4vtet
797525
797524
2025-03-22T19:58:15Z
en>Sophivorus
0
797525
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ -.-:([^]|]-)' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
5ykiweb9570xx36uhtt4hjzp3oexrks
797526
797525
2025-03-22T20:06:36Z
en>Sophivorus
0
Improve getFileName
797526
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *[^:]+: *([^]|]*)' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
43c5tpw8ljdref378ddvkibt4lxqxv0
797527
797526
2025-03-22T20:07:48Z
en>Sophivorus
0
Improve getFileName
797527
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *[^:]+: *(.*) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
8z4tt0l27fmr3wq496n97hjdv8yw17z
797528
797527
2025-03-22T20:08:42Z
en>Sophivorus
0
Improve getFileName
797528
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *[^:]+: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
i1ykv8ihjm94bmbuzvqxedyqrsd7id1
797529
797528
2025-03-22T20:11:41Z
en>Sophivorus
0
Improve getFiles
797529
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because both opening and closing <section> tags are self-closing tags.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = tagOpen:match( '< ?(.-)[ >]' )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = tag:match( '< ?(.-)[ >]' )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
c24cey3edgbp8lvioniz3aiodyvkaue
797530
797529
2025-03-23T21:40:59Z
en>Sophivorus
0
Add getTagName and getTagAttribute
797530
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
function WikitextParser.getTagName( tag )
return tag:match( '^< *(.-)[ >]' )
end
function WikitextParser.getTagAttribute( tag, attribute )
return tag:match( '^< *(.-) *[^>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ >]' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
difqxfl7do2j4948emw3iyta6tnwlqp
797531
797530
2025-03-23T21:49:15Z
en>Sophivorus
0
797531
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
function WikitextParser.getTagName( tag )
return tag:match( '^< *(.-)[ />]' )
end
function WikitextParser.getTagAttribute( tag, attribute )
return tag:match( '^< *(.-) *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
ltbjospy1zvtx9vk2fm7k2f1m8kq6yx
797532
797531
2025-03-23T21:53:06Z
en>Sophivorus
0
Add docs
797532
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the given tag
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tag )
return tag:match( '^< *(.-)[ />]' )
end
-- Get an attribute value from the given tag
-- @param tag Required. Tag to parse.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tag, attribute )
return tag:match( '^< *(.-) *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
g8muzkhkx3jtg44ofhb2z9f3laexfkt
797533
797532
2025-03-23T22:01:16Z
en>Sophivorus
0
797533
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the given tag
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tag )
return tag:match( '^< *(.-)[ />]' )
end
-- Get an attribute value from the given tag
-- @param tag Required. Tag to parse.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tag, attribute )
return tag:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
k1n6kpd1o109s84llnt7gta2pgryf97
797534
797533
2025-03-23T22:03:39Z
en>Sophivorus
0
797534
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the given tag
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tag )
return tag:match( '^< *(.-)[ />]' )
end
-- Get the value of an attribute in the given tag
-- @param tag Required. Tag to parse.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tag, attribute )
return tag:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( t )
return string.match( t, '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
7grrch5in637kapf1c7krzonxijaeto
797535
797534
2025-03-23T22:24:44Z
en>Sophivorus
0
797535
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
return tagWikitext:match( '^< *(.-)[ />]' )
end
-- Get the value of an attribute in the given tag.
-- @param tag Required. Tag to parse.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( tableWikitext )
return tableWikitext:match( '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTableById( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
2t1r2scga1wzmye4u088oq66gcru681
797536
797535
2025-03-23T22:46:02Z
en>Sophivorus
0
Rename getTableById to getTable
797536
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
return tagWikitext:match( '^< *(.-)[ />]' )
end
-- Get the value of an attribute in the given tag.
-- @param tag Required. Tag to parse.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param t Required. Wikitext of the table to parse.
-- @return Id of the table or nil if not found
function WikitextParser.getTableId( tableWikitext )
return tableWikitext:match( '^{|[^\n]-id *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableId( t ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
fc4bra9nnq0pyk50aqr1h3ju59x61vq
797537
797536
2025-03-23T22:55:15Z
en>Sophivorus
0
Generalize getTableId to getTableAttribute
797537
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
return tagWikitext:match( '^< *(.-)[ />]' )
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return tableWikitext:match( '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
5q6h7nq7jietu3m0v952ghlnqj584sq
797538
797537
2025-03-25T14:35:41Z
en>Sophivorus
0
797538
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/!].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag (in lowercase) or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = tagWikitext:match( '^< *(.-)[ />]' )
if tagName then tagName = tagName:lower() end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return tagWikitext:match( '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return tableWikitext:match( '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
bqeno0iqaqv48muk6y95zgvl6fuw71q
797539
797538
2025-03-25T14:56:20Z
en>Sophivorus
0
Refine getTags
797539
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart ) - 1
tag = wikitext:sub( tagStart, tagEnd )
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = tagWikitext:match( '^< *(.-)[ />]' )
if tagName then tagName = tagName:lower() end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return tagWikitext:match( '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return tableWikitext:match( '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
f8wbu08intplh9qyciky78ngkcgn2ww
797540
797539
2025-03-25T15:20:33Z
en>Sophivorus
0
Refine getTags
797540
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = wikitext:sub( tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = tagWikitext:match( '^< *(.-)[ />]' )
if tagName then tagName = tagName:lower() end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return tagWikitext:match( '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return tableWikitext:match( '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
jyd206rs73g3bsaczdxuk04t6k98msi
797541
797540
2025-03-25T15:27:22Z
en>Sophivorus
0
Code style
797541
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = wikitext:sub( tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = tagWikitext:match( '^< *(.-)[ />]' )
if tagName then tagName = tagName:lower() end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return tagWikitext:match( '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return tableWikitext:match( '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = tableWikitext:gsub( '^{|.-\n', '' ) -- remove the header
tableWikitext = tableWikitext:gsub( '\n|}$', '' ) -- remove the footer
tableWikitext = tableWikitext:gsub( '^|%+.-\n', '' ) -- remove any caption
tableWikitext = tableWikitext:gsub( '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = tableWikitext:gsub( '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = tableWikitext:gsub( '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( '|-', true ) do
local rowData = {}
rowWikitext = rowWikitext:gsub( '||', '\n|' )
rowWikitext = rowWikitext:gsub( '!!', '\n|' )
rowWikitext = rowWikitext:gsub( '\n!', '\n|' )
rowWikitext = rowWikitext:gsub( '^!', '\n|' )
rowWikitext = rowWikitext:gsub( '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
5csps4zwhexih6afny9i00wdvtm1c79
797542
797541
2025-05-08T12:35:28Z
en>Sophivorus
0
Fix getTagAttribute
797542
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = wikitext:sub( tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = tagWikitext:match( '^< *(.-)[ />]' )
if tagName then tagName = tagName:lower() end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'/>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return tagWikitext:match( '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return tableWikitext:match( '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = tableWikitext:gsub( '^{|.-\n', '' ) -- remove the header
tableWikitext = tableWikitext:gsub( '\n|}$', '' ) -- remove the footer
tableWikitext = tableWikitext:gsub( '^|%+.-\n', '' ) -- remove any caption
tableWikitext = tableWikitext:gsub( '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = tableWikitext:gsub( '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = tableWikitext:gsub( '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( '|-', true ) do
local rowData = {}
rowWikitext = rowWikitext:gsub( '||', '\n|' )
rowWikitext = rowWikitext:gsub( '!!', '\n|' )
rowWikitext = rowWikitext:gsub( '\n!', '\n|' )
rowWikitext = rowWikitext:gsub( '^!', '\n|' )
rowWikitext = rowWikitext:gsub( '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
dtfl65kdyua4pvv68xnac62qj1qow3z
797543
797542
2025-05-08T13:55:56Z
en>Sophivorus
0
Refine paragraph matching
797543
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = wikitext:gsub( '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in wikitext:gmatch( '\n==+ *([^=]-) *==+' ) do
local section = wikitext:match( '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = wikitext:match( '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = wikitext:gsub( nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = wikitext:match( '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in wikitext:gmatch( '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = wikitext:gsub( '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = wikitext:gsub( '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = wikitext:gsub( '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = wikitext:gsub( '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = wikitext:gsub( '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in wikitext:gmatch( '{%b{}}' ) do
if wikitext:sub( 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return templateWikitext:match( '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = templateWikitext:match( '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in params:gmatch( '{%b{}}' ) do
params = params:gsub( escapeString( subtemplate ), subtemplate:gsub( '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in params:gmatch( '%[%b[]%]' ) do
params = params:gsub( escapeString( link ), link:gsub( '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = value:gsub( '@@_@@', '=' )
value = value:gsub( '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in wikitext:gmatch( '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if tagOpen:match( '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = wikitext:match( '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = wikitext:sub( tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = wikitext:match( '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = wikitext:sub( tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = tagWikitext:match( '^< *(.-)[ />]' )
if tagName then tagName = tagName:lower() end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return tagWikitext:match( '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'/>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return tagWikitext:match( '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in wikitext:gmatch( '\n%b{}' ) do
if t:sub( 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return tableWikitext:match( '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = tableWikitext:gsub( '^{|.-\n', '' ) -- remove the header
tableWikitext = tableWikitext:gsub( '\n|}$', '' ) -- remove the footer
tableWikitext = tableWikitext:gsub( '^|%+.-\n', '' ) -- remove any caption
tableWikitext = tableWikitext:gsub( '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = tableWikitext:gsub( '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = tableWikitext:gsub( '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( '|-', true ) do
local rowData = {}
rowWikitext = rowWikitext:gsub( '||', '\n|' )
rowWikitext = rowWikitext:gsub( '!!', '\n|' )
rowWikitext = rowWikitext:gsub( '\n!', '\n|' )
rowWikitext = rowWikitext:gsub( '^!', '\n|' )
rowWikitext = rowWikitext:gsub( '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return fileWikitext:match( '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = link:match( '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in wikitext:gmatch( '%b[]' ) do
if link:match( '^%[//' ) or link:match( '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
2spfb7r1hww3tuhaz24ue2l6ggqou6u
797544
797543
2025-05-09T14:15:50Z
en>Sophivorus
0
Make string calls explicit for easier debugging
797544
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return string.match( tagWikitext, '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'/>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( gmatch, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
i7pjsajw9ebjrx3e2929yuww73toflg
797545
797544
2025-05-09T14:16:51Z
en>Sophivorus
0
797545
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return string.match( tagWikitext, '^< *.- *[^/>]*' .. attribute .. ' *= *["\']?([^"\'/>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
oolrhgdob9s82agoctij7s546klops6
797546
797545
2025-05-09T18:18:14Z
en>Aidan9382
0
singificantly improve the performance of getTagAttribute (prevent the .- from trying to scan everything after the opening tag when the attribute isn't present) (also just simplified it in general to avoid repeated ambiguous captures)
797546
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
return string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *["\']?([^"\'/>]+)["\']?[ />]' )
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil is not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
return string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *["\']?([^"\'\n]+)["\']?[^\n]*\n' )
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
m2pw760dpfxn7l36pi0649bw0m09x6l
797547
797546
2025-05-09T18:28:23Z
en>Aidan9382
0
Slightly improve getXYZAttribute when getting string-wrapped content (this should allow cases like <ref name="xyz's abc"> to capture name, though other cases like \-escaped quotes will still break)
797547
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _quote, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _quote, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
c21w4rau9v8rya4xm6lvmpj93yrm8pl
797548
797547
2025-06-20T13:57:00Z
en>Sophivorus
0
Add missing param
797548
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _quote, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _quote, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
tmqgmfv09xj7f50m3k9l4o4b8efibdg
797549
797548
2025-10-02T18:00:44Z
en>MusikBot II
0
Protected "[[Module:WikitextParser]]": [[Wikipedia:High-risk templates|High-risk template or module]]: 11524 transclusions ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
797548
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( wikitext, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _quote, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _quote, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
tmqgmfv09xj7f50m3k9l4o4b8efibdg
797550
797549
2025-10-02T18:32:43Z
en>Aidan9382
0
wrong variable used in getTemplates check
797550
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _quote, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _quote, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
f88uhct4rmxdk9ohkcqh8ma54hr84u6
797551
797550
2025-10-03T12:27:15Z
en>Sophivorus
0
Move list removal below to fix the bug reported at [[Module talk:Excerpt#Bug report: poss interference from escaped pipe earlier on page]]
797551
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _quote, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext, attribute )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _quote, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
qf73q03ka5bli5zsckgxc9imrhotias
797552
797551
2025-10-06T12:29:27Z
en>Sophivorus
0
Refine getParagraphs
797552
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n'
wikitext = string.gsub( wikitext, '<!%-%-.-%-%->', '' ) -- remove comments
wikitext = string.gsub( wikitext, '\n%[%b[]%]\n', '\n' ) -- remove files and categories
wikitext = string.gsub( wikitext, '\n(%b{}) *(%b{}) *\n', '\n%1\n%2\n' ) -- separate neighboring tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n%0\n' ) -- add spacing between tables and block templates
wikitext = string.gsub( wikitext, '\n%b{} *\n', '\n' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '\n[*#][^\n]*', '\n' ) -- remove lists
wikitext = string.gsub( wikitext, '\n==+[^=]+==+ *\n', '\n' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _quote, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _quote, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
gl3n4krdkr4sfrd4istee98ixrolxlt
797553
797552
2025-10-21T12:42:50Z
en>Sophivorus
0
Simplify and improve getParagraphs by using frontier patterns
797553
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
wikitext = string.match( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' )
if wikitext then
return mw.text.trim( wikitext )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n' -- add newlines to simplfy patterns
wikitext = string.gsub( wikitext, '%f[^\n]<!%-%-.-%-%->%f[\n]', '' ) -- remove comments
wikitext = string.gsub( wikitext, '%f[^\n]%[%b[]%]%f[\n]', '' ) -- remove files and categories
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%f[\n]', '' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n](%b{}) *(%b{}) *%f[\n]', '' ) -- remove neighboring tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n](%b{}) *<!%-%-.-%-%-> *(%b{}) *%f[\n]', '' ) -- remove neighboring tables and block templates with a comment among them
wikitext = string.gsub( wikitext, '%f[^\n][*#].-%f[\n]', '' ) -- remove lists
wikitext = string.gsub( wikitext, '%f[^\n]==+[^=]+==+ *%f[\n]', '' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _quote, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _quote, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
rs9tzr0m21sihr2maqoyitxah6ls278
797554
797553
2025-11-03T12:54:38Z
en>Sophivorus
0
Support repeated section tags
797554
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
local sections = {}
for section in string.gmatch( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' ) do
table.insert( sections, section )
end
if #sections > 0 then
return table.concat( sections )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n' -- add newlines to simplfy patterns
wikitext = string.gsub( wikitext, '%f[^\n]<!%-%-.-%-%->%f[\n]', '' ) -- remove comments
wikitext = string.gsub( wikitext, '%f[^\n]%[%b[]%]%f[\n]', '' ) -- remove files and categories
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%f[\n]', '' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *<!%-%-.-%-%-> *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates with a comment among them
wikitext = string.gsub( wikitext, '%f[^\n][*#].-%f[\n]', '' ) -- remove lists
wikitext = string.gsub( wikitext, '%f[^\n]==+[^=]+==+ *%f[\n]', '' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _quote, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _quote, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
h5pgr9hqwjcsn2eqsnegqry14h34fqq
797555
797554
2026-01-17T13:40:07Z
en>Sophivorus
0
Avoid Lua linting errors
797555
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local WikitextParser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function WikitextParser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function WikitextParser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function WikitextParser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level
level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function WikitextParser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
local sections = {}
for section in string.gmatch( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' ) do
table.insert( sections, section )
end
if #sections > 0 then
return table.concat( sections )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function WikitextParser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function WikitextParser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n' -- add newlines to simplfy patterns
wikitext = string.gsub( wikitext, '%f[^\n]<!%-%-.-%-%->%f[\n]', '' ) -- remove comments
wikitext = string.gsub( wikitext, '%f[^\n]%[%b[]%]%f[\n]', '' ) -- remove files and categories
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%f[\n]', '' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *<!%-%-.-%-%-> *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates with a comment among them
wikitext = string.gsub( wikitext, '%f[^\n][*#].-%f[\n]', '' ) -- remove lists
wikitext = string.gsub( wikitext, '%f[^\n]==+[^=]+==+ *%f[\n]', '' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function WikitextParser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function WikitextParser.getTemplate( wikitext, name )
local templates = WikitextParser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = WikitextParser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function WikitextParser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function WikitextParser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function WikitextParser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = WikitextParser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function WikitextParser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTagAttribute( tagWikitext, attribute )
local _, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function WikitextParser.getTagContent( tagWikitext )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function WikitextParser.getGalleries( wikitext )
local galleries = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function WikitextParser.getReferences( wikitext )
local references = {}
local tags = WikitextParser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = WikitextParser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function WikitextParser.getReference( wikitext, referenceName )
local references = WikitextParser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = WikitextParser.getTagContent( reference )
local name = WikitextParser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function WikitextParser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function WikitextParser.getTableAttribute( tableWikitext, attribute )
local _, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function WikitextParser.getTable( wikitext, id )
local tables = WikitextParser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == WikitextParser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function WikitextParser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function WikitextParser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function WikitextParser.getFiles( wikitext )
local files = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function WikitextParser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function WikitextParser.getCategories( wikitext )
local categories = {}
local links = WikitextParser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function WikitextParser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return WikitextParser
cg08iqs1angnfozr3nne9xyalcy0zao
797556
797555
2026-02-11T14:45:12Z
en>Sophivorus
0
Rename variable "WikitextParser" to just "parser" for simplicty, and fix a few edge cases
797556
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local parser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function parser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function parser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function parser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level
level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function parser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
local sections = {}
for section in string.gmatch( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' ) do
table.insert( sections, section )
end
if #sections > 0 then
return table.concat( sections )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function parser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function parser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n' -- add newlines to simplfy patterns
wikitext = string.gsub( wikitext, '%f[^\n]<!%-%-.-%-%->%f[\n]', '' ) -- remove comments
wikitext = string.gsub( wikitext, '%f[^\n]%[%b[]%]%f[\n]', '' ) -- remove files and categories
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%f[\n]', '' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *<!%-%-.-%-%-> *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates with a comment among them
wikitext = string.gsub( wikitext, '%f[^\n][*#].-%f[\n]', '' ) -- remove lists
wikitext = string.gsub( wikitext, '%f[^\n]==+[^=]+==+ *%f[\n]', '' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function parser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function parser.getTemplate( wikitext, name )
local templates = parser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = parser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function parser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function parser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if tonumber( name ) then
name = tonumber( name )
end
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function parser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = parser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function parser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function parser.getTagAttribute( tagWikitext, attribute )
local _, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function parser.getTagContent( tagWikitext )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function parser.getGalleries( wikitext )
local galleries = {}
local tags = parser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = parser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function parser.getReferences( wikitext )
local references = {}
local tags = parser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = parser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function parser.getReference( wikitext, referenceName )
local references = parser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = parser.getTagContent( reference )
local name = parser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function parser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function parser.getTableAttribute( tableWikitext, attribute )
local _, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
if not value or value == '' then
value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *([^\n ]+)[^\n]*\n' )
end
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function parser.getTable( wikitext, id )
local tables = parser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == parser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function parser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function parser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function parser.getFiles( wikitext )
local files = {}
local links = parser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function parser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function parser.getCategories( wikitext )
local categories = {}
local links = parser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function parser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return parser
m6egne6d445gid50doxwuxuw8b4hok5
797557
797556
2026-06-08T20:27:21Z
SM7
3953
191 revisions imported from [[:en:Module:WikitextParser]]
797556
Scribunto
text/plain
-- Module:WikitextParser is a general-purpose wikitext parser
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al.
-- License: CC-BY-SA-4.0
local parser = {}
-- Private helper method to escape a string for use in regexes
local function escapeString( str )
return string.gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' )
end
-- Get the lead section from the given wikitext
-- The lead section is any content before the first section title.
-- @param wikitext Required. Wikitext to parse.
-- @return Wikitext of the lead section. May be empty if the lead section is empty.
function parser.getLead( wikitext )
wikitext = '\n' .. wikitext
wikitext = string.gsub( wikitext, '\n==.*', '' )
wikitext = mw.text.trim( wikitext )
return wikitext
end
-- Get the sections from the given wikitext
-- This method doesn't get the lead section, use getLead for that
-- @param wikitext Required. Wikitext to parse.
-- @return Map from section title to section content
function parser.getSections( wikitext )
local sections = {}
wikitext = '\n' .. wikitext .. '\n=='
for title in string.gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do
local section = string.match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' )
section = mw.text.trim( section )
sections[ title ] = section
end
return sections
end
-- Get a section from the given wikitext (including any subsections)
-- If the given section title appears more than once, only the section of the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param title Required. Title of the section
-- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections.
function parser.getSection( wikitext, title )
title = mw.text.trim( title )
title = escapeString( title )
wikitext = '\n' .. wikitext .. '\n'
local level
level, wikitext = string.match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' )
if wikitext then
local nextSection = '\n==' .. string.rep( '=?', #level - 2 ) .. '[^=].*'
wikitext = string.gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher
wikitext = mw.text.trim( wikitext )
return wikitext
end
end
-- Get the content of a <section> tag from the given wikitext.
-- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing.
-- @param wikitext Required. Wikitext to parse.
-- @param name Required. Name of the <section> tag
-- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty.
function parser.getSectionTag( wikitext, name )
name = mw.text.trim( name )
name = escapeString( name )
local sections = {}
for section in string.gmatch( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' ) do
table.insert( sections, section )
end
if #sections > 0 then
return table.concat( sections )
end
end
-- Get the lists from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of lists.
function parser.getLists( wikitext )
local lists = {}
wikitext = '\n' .. wikitext .. '\n\n'
for list in string.gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do
table.insert( lists, list )
end
return lists
end
-- Get the paragraphs from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of paragraphs.
function parser.getParagraphs( wikitext )
local paragraphs = {}
-- Remove non-paragraphs
wikitext = '\n' .. wikitext .. '\n' -- add newlines to simplfy patterns
wikitext = string.gsub( wikitext, '%f[^\n]<!%-%-.-%-%->%f[\n]', '' ) -- remove comments
wikitext = string.gsub( wikitext, '%f[^\n]%[%b[]%]%f[\n]', '' ) -- remove files and categories
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%f[\n]', '' ) -- remove tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates
wikitext = string.gsub( wikitext, '%f[^\n]%b{} *<!%-%-.-%-%-> *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates with a comment among them
wikitext = string.gsub( wikitext, '%f[^\n][*#].-%f[\n]', '' ) -- remove lists
wikitext = string.gsub( wikitext, '%f[^\n]==+[^=]+==+ *%f[\n]', '' ) -- remove section titles
wikitext = mw.text.trim( wikitext )
for paragraph in mw.text.gsplit( wikitext, '\n\n+' ) do
if mw.text.trim( paragraph ) ~= '' then
table.insert( paragraphs, paragraph )
end
end
return paragraphs
end
-- Get the templates from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of templates.
function parser.getTemplates( wikitext )
local templates = {}
for template in string.gmatch( wikitext, '{%b{}}' ) do
if string.sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if
table.insert( templates, template )
end
end
return templates
end
-- Get the requested template from the given wikitext.
-- If the template appears more than once, only the first instance will be returned
-- @param wikitext Required. Wikitext to parse.
-- @param name Name of the template to get
-- @return Wikitext of the template, or nil if it wasn't found
function parser.getTemplate( wikitext, name )
local templates = parser.getTemplates( wikitext )
local lang = mw.language.getContentLanguage()
for _, template in pairs( templates ) do
local templateName = parser.getTemplateName( template )
if lang:ucfirst( templateName ) == lang:ucfirst( name ) then
return template
end
end
end
-- Get name of the template from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Name of the template
-- @todo Strip "Template:" namespace?
function parser.getTemplateName( templateWikitext )
return string.match( templateWikitext, '^{{ *([^}|\n]+)' )
end
-- Get the parameters from the given template wikitext.
-- @param templateWikitext Required. Wikitext of the template to parse.
-- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER.
-- @return Order in which the parameters were parsed.
function parser.getTemplateParameters( templateWikitext )
local parameters = {}
local paramOrder = {}
local params = string.match( templateWikitext, '{{[^|}]-|(.*)}}' )
if params then
-- Temporarily replace pipes in subtemplates and links to avoid chaos
for subtemplate in string.gmatch( params, '{%b{}}' ) do
params = string.gsub( params, escapeString( subtemplate ), string.gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ) )
end
for link in string.gmatch( params, '%[%b[]%]' ) do
params = string.gsub( params, escapeString( link ), string.gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ) )
end
local count = 0
local parts, name, value
for param in mw.text.gsplit( params, '|' ) do
parts = mw.text.split( param, '=' )
name = mw.text.trim( parts[1] )
if tonumber( name ) then
name = tonumber( name )
end
if #parts == 1 then
value = name
count = count + 1
name = count
else
value = table.concat( parts, '=', 2 );
value = mw.text.trim( value )
end
value = string.gsub( value, '@@_@@', '=' )
value = string.gsub( value, '@@:@@', '|' )
parameters[ name ] = value
table.insert( paramOrder, name )
end
end
return parameters, paramOrder
end
-- Get the tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tags.
function parser.getTags( wikitext )
local tags = {}
local tag, tagName, tagEnd
-- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3
for tagStart, tagOpen in string.gmatch( wikitext, '()(<[^/!%d].->)' ) do
tagName = parser.getTagName( tagOpen )
-- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc.
if string.match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then
tag = tagOpen
-- If we're in a tag that may contain others like it, like <div> or <span>
elseif tagName == 'div' or tagName == 'span' then
local position = tagStart + #tagOpen - 1
local depth = 1
while depth > 0 do
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', position )
if tagEnd then
tagEnd = tagEnd - 1
else
break -- unclosed tag
end
position = string.match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 )
if not position then
position = tagEnd + 1
end
if position > tagEnd then
depth = depth - 1
else
depth = depth + 1
end
end
tag = string.sub( wikitext, tagStart, tagEnd )
-- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong>
else
tagEnd = string.match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart )
if tagEnd then
tag = string.sub( wikitext, tagStart, tagEnd - 1 )
-- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1>
else
tag = nil
end
end
table.insert( tags, tag )
end
return tags
end
-- Get the name of the tag in the given wikitext
-- @param tag Required. Tag to parse.
-- @return Name of the tag or nil if not found
function parser.getTagName( tagWikitext )
local tagName = string.match( tagWikitext, '^< *(.-)[ />]' )
if tagName then tagName = string.lower( tagName ) end
return tagName
end
-- Get the value of an attribute in the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function parser.getTagAttribute( tagWikitext, attribute )
local _, value = string.match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' )
return value
end
-- Get the content of the given tag.
-- @param tagWikitext Required. Wikitext of the tag to parse.
-- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing.
-- @todo May fail with nested tags
function parser.getTagContent( tagWikitext )
return string.match( tagWikitext, '^<.->.-</.->' )
end
-- Get the <gallery> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of gallery tags.
function parser.getGalleries( wikitext )
local galleries = {}
local tags = parser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = parser.getTagName( tag )
if tagName == 'gallery' then
table.insert( galleries, tag )
end
end
return galleries
end
-- Get the <ref> tags from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of ref tags.
function parser.getReferences( wikitext )
local references = {}
local tags = parser.getTags( wikitext )
for _, tag in pairs( tags ) do
local tagName = parser.getTagName( tag )
if tagName == 'ref' then
table.insert( references, tag )
end
end
return references
end
-- Get the reference with the given name from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @param referenceName Required. Name of the reference.
-- @return Wikitext of the reference
function parser.getReference( wikitext, referenceName )
local references = parser.getReferences( wikitext )
for _, reference in pairs( references ) do
local content = parser.getTagContent( reference )
local name = parser.getTagAttribute( reference, 'name' )
if content and name == referenceName then
return reference
end
end
end
-- Get the tables from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of tables.
function parser.getTables( wikitext )
local tables = {}
wikitext = '\n' .. wikitext
for t in string.gmatch( wikitext, '\n%b{}' ) do
if string.sub( t, 1, 3 ) == '\n{|' then
t = mw.text.trim( t ) -- exclude the leading newline
table.insert( tables, t )
end
end
return tables
end
-- Get the id from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @param attribute Required. Name of the attribute.
-- @return Value of the attribute or nil if not found
function parser.getTableAttribute( tableWikitext, attribute )
local _, value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' )
if not value or value == '' then
value = string.match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *([^\n ]+)[^\n]*\n' )
end
return value
end
-- Get a table by id from the given wikitext
-- @param wikitext Required. Wikitext to parse.
-- @param id Required. Id of the table
-- @return Wikitext of the table or nil if not found
function parser.getTable( wikitext, id )
local tables = parser.getTables( wikitext )
for _, t in pairs( tables ) do
if id == parser.getTableAttribute( t, 'id' ) then
return t
end
end
end
-- Get the data from the given table wikitext
-- @param tableWikitext Required. Wikitext of the table to parse.
-- @return Table data
-- @todo Test and make more robust
function parser.getTableData( tableWikitext )
local tableData = {}
tableWikitext = mw.text.trim( tableWikitext );
tableWikitext = string.gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header
tableWikitext = string.gsub( tableWikitext, '\n|}$', '' ) -- remove the footer
tableWikitext = string.gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption
tableWikitext = string.gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes
tableWikitext = string.gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row
tableWikitext = string.gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row
for rowWikitext in mw.text.gsplit( tableWikitext, '|-', true ) do
local rowData = {}
rowWikitext = string.gsub( rowWikitext, '||', '\n|' )
rowWikitext = string.gsub( rowWikitext, '!!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '\n!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^!', '\n|' )
rowWikitext = string.gsub( rowWikitext, '^\n|', '' )
for cellWikitext in mw.text.gsplit( rowWikitext, '\n|' ) do
cellWikitext = mw.text.trim( cellWikitext )
table.insert( rowData, cellWikitext )
end
table.insert( tableData, rowData )
end
return tableData
end
-- Get the internal links from the given wikitext (includes category and file links).
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of internal links.
function parser.getLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%[%b[]%]' ) do
table.insert( links, link )
end
return links
end
-- Get the file links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of file links.
function parser.getFiles( wikitext )
local files = {}
local links = parser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ *(.-) *:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then
table.insert( files, link )
end
end
return files
end
-- Get name of the file from the given file wikitext.
-- @param fileWikitext Required. Wikitext of the file to parse.
-- @return Name of the file
function parser.getFileName( fileWikitext )
return string.match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' )
end
-- Get the category links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of category links.
function parser.getCategories( wikitext )
local categories = {}
local links = parser.getLinks( wikitext )
for _, link in pairs( links ) do
local namespace = string.match( link, '^%[%[ -(.-) -:' )
if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then
table.insert( categories, link )
end
end
return categories
end
-- Get the external links from the given wikitext.
-- @param wikitext Required. Wikitext to parse.
-- @return Sequence of external links.
function parser.getExternalLinks( wikitext )
local links = {}
for link in string.gmatch( wikitext, '%b[]' ) do
if string.match( link, '^%[//' ) or string.match( link, '^%[https?://' ) then
table.insert( links, link )
end
end
return links
end
return parser
m6egne6d445gid50doxwuxuw8b4hok5
टेम्पलेट:Avoid wrap
10
101022
797570
2019-01-02T14:59:14Z
en>TheDJ
0
experiment with avoid wrap
797570
wikitext
text/x-wiki
<span class="avoidwrap" style="display:inline-block;">{{{1}}}</span><noinclude>
{{documentation}}
<!-- Categories go on the /doc page; interwikis go to Wikidata. -->
</noinclude>
t0fisb4vbw5b9iv4j2xygm8zyci9s0r
797571
797570
2022-03-25T04:30:52Z
en>Toolsmasts
0
797571
wikitext
text/x-wiki
<span class="avoidwrap" style="display:inline-block;">{{{1}}}</span><includeonly>[[File:Wedgie.jpg]]<noinclude>
{{documentation}}
<!-- Categories go on the /doc page; interwikis go to Wikidata. -->
</noinclude>
e0sattd37i7ak5vt2fn2zod7zsyciwb
797572
797571
2022-03-25T04:37:51Z
en>Spicy
0
Reverted edits by [[Special:Contribs/Toolsmasts|Toolsmasts]] ([[User talk:Toolsmasts|talk]]) to last version by TheDJ
797572
wikitext
text/x-wiki
<span class="avoidwrap" style="display:inline-block;">{{{1}}}</span><noinclude>
{{documentation}}
<!-- Categories go on the /doc page; interwikis go to Wikidata. -->
</noinclude>
t0fisb4vbw5b9iv4j2xygm8zyci9s0r
797573
797572
2023-01-22T18:00:08Z
en>MusikBot II
0
Protected "[[Template:Avoid wrap]]": [[Wikipedia:High-risk templates|High-risk template or module]]: 253 transclusions ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require autoconfirmed or confirmed access] (indefinite))
797572
wikitext
text/x-wiki
<span class="avoidwrap" style="display:inline-block;">{{{1}}}</span><noinclude>
{{documentation}}
<!-- Categories go on the /doc page; interwikis go to Wikidata. -->
</noinclude>
t0fisb4vbw5b9iv4j2xygm8zyci9s0r
797574
797573
2024-03-07T18:00:45Z
en>MusikBot II
0
Changed protection settings for "[[Template:Avoid wrap]]": [[Wikipedia:High-risk templates|High-risk template or module]]: 2670 transclusions ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite))
797572
wikitext
text/x-wiki
<span class="avoidwrap" style="display:inline-block;">{{{1}}}</span><noinclude>
{{documentation}}
<!-- Categories go on the /doc page; interwikis go to Wikidata. -->
</noinclude>
t0fisb4vbw5b9iv4j2xygm8zyci9s0r
797575
797574
2026-04-18T03:51:59Z
en>Pppery
0
Changed protection settings for "[[Template:Avoid wrap]]" ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
797572
wikitext
text/x-wiki
<span class="avoidwrap" style="display:inline-block;">{{{1}}}</span><noinclude>
{{documentation}}
<!-- Categories go on the /doc page; interwikis go to Wikidata. -->
</noinclude>
t0fisb4vbw5b9iv4j2xygm8zyci9s0r
797576
797575
2026-06-09T01:28:07Z
SM7
3953
6 revisions imported from [[:en:Template:Avoid_wrap]]
797572
wikitext
text/x-wiki
<span class="avoidwrap" style="display:inline-block;">{{{1}}}</span><noinclude>
{{documentation}}
<!-- Categories go on the /doc page; interwikis go to Wikidata. -->
</noinclude>
t0fisb4vbw5b9iv4j2xygm8zyci9s0r
टेम्पलेट:Avoid wrap/doc
10
101023
797577
2019-01-02T15:03:51Z
en>TheDJ
0
docs
797577
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{High-use| 996942 }}
{{COinS safe|n}}
{{tlx|Avoid wrap}} or {{tlx|avoidwrap}} avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Avoid wrap begin}} + {{tl|Avoid wrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
The class <code>Avoid wrap</code> receives the CSS property {{code|lang=css|white-space: Avoid wrap;}} in [[MediaWiki:Common.css]].
Spaces at the beginning or end of the text will fall outside the no-wrap tag in the rendered text due to Wikimedia rendering mechanisms.
The templates {{tl|nobr}} and {{tl|nobreak}} redirect here.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of Avoid wrap-like templates}}
<includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
5nh6ea2duouth82yixfeeef8jingnoq
797578
797577
2019-01-02T15:07:58Z
en>TheDJ
0
/* Technical details */
797578
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{High-use| 996942 }}
{{COinS safe|n}}
{{tlx|Avoid wrap}} or {{tlx|avoidwrap}} avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Avoid wrap begin}} + {{tl|Avoid wrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
When there is not enough remaining space on a line to fit the contents of such as block, it will be pushed to the next line in its entirety. However when the block in itself doesn't fit on a single line, line breaking still applies to the 'internal' text block. This is especially advantageous for mobile rendering where the available line width might be much smaller than on desktops and might require line breaks to make longer words or sections fit.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of Avoid wrap-like templates}}
<includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
2t7s8ug39h39rbfdzj5ej4kt6tgowel
797579
797578
2019-09-20T23:01:50Z
en>Watchduck
0
797579
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{High-use| 996942 }}
{{COinS safe|n}}
{{tlx|Avoid wrap}}, {{tlx|avoidwrap}} or {{tlx|awrap}} avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Avoid wrap begin}} + {{tl|Avoid wrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
When there is not enough remaining space on a line to fit the contents of such as block, it will be pushed to the next line in its entirety. However when the block in itself doesn't fit on a single line, line breaking still applies to the 'internal' text block. This is especially advantageous for mobile rendering where the available line width might be much smaller than on desktops and might require line breaks to make longer words or sections fit.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of Avoid wrap-like templates}}
<includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
5yxj492uybmul10xk7pw6986oeutcww
797580
797579
2019-09-20T23:03:46Z
en>Watchduck
0
shorthand {{awrap}}
797580
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{High-use| 996942 }}
{{COinS safe|n}}
<code><nowiki>{{Avoid wrap}}</nowiki></code>, <code><nowiki>{{avoidwrap}}</nowiki></code> or <code><nowiki>{{awrap}}</nowiki></code> avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Avoid wrap begin}} + {{tl|Avoid wrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
When there is not enough remaining space on a line to fit the contents of such as block, it will be pushed to the next line in its entirety. However when the block in itself doesn't fit on a single line, line breaking still applies to the 'internal' text block. This is especially advantageous for mobile rendering where the available line width might be much smaller than on desktops and might require line breaks to make longer words or sections fit.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of Avoid wrap-like templates}}
<includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
s01nn3vxgqd3cdahvqu0cfyikdql91j
797581
797580
2019-09-25T04:46:19Z
en>Willy1018
0
797581
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{COinS safe|n}}
<code><nowiki>{{Avoid wrap}}</nowiki></code>, <code><nowiki>{{avoidwrap}}</nowiki></code> or <code><nowiki>{{awrap}}</nowiki></code> avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Avoid wrap begin}} + {{tl|Avoid wrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
When there is not enough remaining space on a line to fit the contents of such as block, it will be pushed to the next line in its entirety. However when the block in itself doesn't fit on a single line, line breaking still applies to the 'internal' text block. This is especially advantageous for mobile rendering where the available line width might be much smaller than on desktops and might require line breaks to make longer words or sections fit.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of Avoid wrap-like templates}}
<includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
q1qys4m7xuq7gep9aah3p3fuxis12em
797582
797581
2020-04-23T23:29:43Z
en>Vanisaac
0
/* See also */clean up per [[WP:CAT#T]] and [[WP:AWBREQ]] add template:Sandbox other
797582
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{COinS safe|n}}
<code><nowiki>{{Avoid wrap}}</nowiki></code>, <code><nowiki>{{avoidwrap}}</nowiki></code> or <code><nowiki>{{awrap}}</nowiki></code> avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Avoid wrap begin}} + {{tl|Avoid wrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
When there is not enough remaining space on a line to fit the contents of such as block, it will be pushed to the next line in its entirety. However when the block in itself doesn't fit on a single line, line breaking still applies to the 'internal' text block. This is especially advantageous for mobile rendering where the available line width might be much smaller than on desktops and might require line breaks to make longer words or sections fit.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of Avoid wrap-like templates}}
<includeonly>{{Sandbox other||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
ctjn8osxt6tllx36v2pzgencu0fdik0
797583
797582
2020-04-28T17:09:18Z
en>Plastikspork
0
/* See also */ Should be added there
797583
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{COinS safe|n}}
<code><nowiki>{{Avoid wrap}}</nowiki></code>, <code><nowiki>{{avoidwrap}}</nowiki></code> or <code><nowiki>{{awrap}}</nowiki></code> avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Avoid wrap begin}} + {{tl|Avoid wrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
When there is not enough remaining space on a line to fit the contents of such as block, it will be pushed to the next line in its entirety. However when the block in itself doesn't fit on a single line, line breaking still applies to the 'internal' text block. This is especially advantageous for mobile rendering where the available line width might be much smaller than on desktops and might require line breaks to make longer words or sections fit.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of nowrap-like templates}}
<includeonly>{{Sandbox other||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
gx6ow6d0yolwq8lww4sb76xua9u4097
797584
797583
2025-04-12T05:44:40Z
en>Joeyconnick
0
/* Handling equal-sign or bar */ list actual templates
797584
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{COinS safe|n}}
<code><nowiki>{{Avoid wrap}}</nowiki></code>, <code><nowiki>{{avoidwrap}}</nowiki></code> or <code><nowiki>{{awrap}}</nowiki></code> avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Nowrap begin}} + {{tl|Nowrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
When there is not enough remaining space on a line to fit the contents of such as block, it will be pushed to the next line in its entirety. However when the block in itself doesn't fit on a single line, line breaking still applies to the 'internal' text block. This is especially advantageous for mobile rendering where the available line width might be much smaller than on desktops and might require line breaks to make longer words or sections fit.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of nowrap-like templates}}
<includeonly>{{Sandbox other||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
cez8bzef10nkdxt8uizzfogdp00cehc
797585
797584
2026-06-09T01:28:27Z
SM7
3953
8 revisions imported from [[:en:Template:Avoid_wrap/doc]]
797584
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Categories go where indicated at the bottom of this page, please; interwikis go to Wikidata (see also: [[Wikipedia:Wikidata]]) -->
{{COinS safe|n}}
<code><nowiki>{{Avoid wrap}}</nowiki></code>, <code><nowiki>{{avoidwrap}}</nowiki></code> or <code><nowiki>{{awrap}}</nowiki></code> avoids wrapping of specific text.
To achieve the opposite effect of <code>{{Avoid wrap}}</code>, you can use {{tlx|wbr}}. For more information about wrapping and breaking sentences, see [[Wikipedia:Line-break handling]].
== Usage ==
:: <code><nowiki>{{Avoid wrap|these words stay together}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap|a, b, c, or d.}}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| merry-go-round }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[4-part harmony]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[Cascading Style Sheets|CSS]] }}</nowiki></code>
:: <code><nowiki>{{Avoid wrap| [[#Examples|Examples section]] }}</nowiki></code>
The template names {{tl|Nobr}} and {{tl|Nobreak}} are redirects that may be used instead of "{{tlc|Avoid wrap|…}}".
=== Examples ===
{| class=wikitable
|"<code>They held <nowiki>{{Avoid wrap|10 kg (22 lb)}}</nowiki> in total.</code>"
<br/> May render like this:
:They held {{Avoid wrap|10 kg (22 lb)}} <br/>in total.
<br/> Or like this:
:They held <br/>10 kg (22 lb) in total.
<br/> But ''not'' render like this:
:They held 10 kg (22 <br/>lb) in total.
| "<code>He encountered <nowiki>{{Avoid wrap|a tiger}}</nowiki> in the woods.</code>"
<br/> May render like this:
:He encountered a tiger <br/>in the woods.
<br/> Or like this:
:He encountered <br/>a tiger in the woods.
<br/> But ''not'' like this:
:He encountered a <br/>tiger in the woods.
|}
=== Controlling line-breaking in infoboxes ===
This template may be used with {{tlx|Wbr}} and {{tlx|Spaces}} to control line-breaking in bulletless lists in infoboxes, to prevent wrapped long entries from being confused with multiple entries. See [[Template:Wbr/doc#Controlling line-breaking in infoboxes]] for details.
=== Handling equal-sign or bar ===
[[Help:Template|Templates]] have problems with parameter data that contains [[equal-sign]]s "<code>=</code>" or vertical bars "<code>|</code>" (pipes). Note that this does not apply if the equal-sign "<code>=</code>" or vertical bar "<code>|</code>" is inside a wikilink or another template. In the case that there is a bare equal-sign "<code>=</code>" or vertical bar "<code>|</code>", consider using {{tl|Nowrap begin}} + {{tl|Nowrap end}} instead. However, there are also other workarounds:
For text that includes an equal-sign "=", precede the text with <code>1=</code>, use a triple-brace unnamed parameter <code><nowiki>{{{|=}}}</nowiki></code>, or the <code><nowiki>{{=}}</nowiki></code> template. For example:
:<code><nowiki>{{Avoid wrap|</nowiki>1=2 + 2 = 4}}</code>, <br /><code><nowiki>{{Avoid wrap|2 + 2 {{{|=}}} 4}}</nowiki></code>, or <br /><code><nowiki>{{Avoid wrap|2 + 2 {{=}} 4}}</nowiki></code>
which all render as this:
:{{Avoid wrap|1=2 + 2 = 4}}.
For text that includes a vertical bar "|", escape the bar(s) with "<code><nowiki>&#124;</nowiki></code>" or "<code><nowiki>{{pipe}}</nowiki></code>" or "<code><nowiki>{{abs}}</nowiki></code>". For instance, put two bars "|6|" like this:
:<code><nowiki>{{Avoid wrap|</nowiki>&#124;6&#124; < 7}}</code> or <code><nowiki>{{Avoid wrap|{{pipe}}6{{pipe}} < 7}}</nowiki></code> or <code><nowiki>{{avoid wrap|{{abs|6}} < 7}}</nowiki></code>
Which renders this:
:{{avoid wrap||6| < 7}}
== Technical details ==
The actual code that does the job is this [[HTML]] span tag that applies a class to the text inside the template:
:{{code|lang=html|1=<span class="avoidwrap" style="display:inline-block;">This text will not wrap</span>}}
When there is not enough remaining space on a line to fit the contents of such as block, it will be pushed to the next line in its entirety. However when the block in itself doesn't fit on a single line, line breaking still applies to the 'internal' text block. This is especially advantageous for mobile rendering where the available line width might be much smaller than on desktops and might require line breaks to make longer words or sections fit.
== TemplateData ==
{{TemplateData header}}
<templatedata>{
"description": "Prevents word wraps (line breaks) within text or inside a link which contains spaces or hyphens (-).",
"params": {
"1": {
"label": "Text",
"description": "Text or link to be protected.",
"type": "string",
"required": true
}
}
}</templatedata>
== See also ==
{{List of nowrap-like templates}}
<includeonly>{{Sandbox other||
<!-- Categories go below this line, please; interwikis go to Wikidata, thank you! -->
[[Category:Line-handling templates]]
}}</includeonly>
cez8bzef10nkdxt8uizzfogdp00cehc
विकिपीडिया:Moving a page
4
101024
797594
2026-06-09T03:45:17Z
SM7
3953
पन्ना [[विकिपीडिया:पन्ना के स्थानांतरण कइल]] पर अनुप्रेषित कइल गइल
797594
wikitext
text/x-wiki
#REDIRECT [[विकिपीडिया:पन्ना के स्थानांतरण कइल]]
bkidz1s7n06ba8oqubg5l16fl3tb728
विकिपीडिया:Requested moves/Closing instructions
4
101025
797595
2026-06-09T03:51:22Z
SM7
3953
पन्ना बनावल गइल "{{Explanatory supplement|interprets=Wikipedia:Requested moves|shortcut=WP:RMCI|shortcut2=WP:RMCLOSE}} {{Nutshell|#Don't close requested moves if you are involved; #If you are not an administrator you should be cautious when closing contentious requests; #Determine [[WP:CONSENSUS|consensus]] on the request or relist it for further discussion; #Investigate the page history of the target page title; if ''minor'', it may be deleted; i..." के साथ
797595
wikitext
text/x-wiki
{{Explanatory supplement|interprets=Wikipedia:Requested moves|shortcut=WP:RMCI|shortcut2=WP:RMCLOSE}}
{{Nutshell|#Don't close requested moves if you are involved;
#If you are not an administrator you should be cautious when closing contentious requests;
#Determine [[WP:CONSENSUS|consensus]] on the request or relist it for further discussion;
#Investigate the page history of the target page title; if ''minor'', it may be deleted; if ''major'', perform a [[Wikipedia:How to fix cut-and-paste moves|history merge]], [[WP:SWAP|history swap]], or archive and place a link on the talk page;
#Close the move request on the talk page using {{tls|RM top}} and {{tls|RM bottom}};
#If renamed then [[WP:POSTMOVE|clean up after the move]]
}}
{{Simple|Wikipedia:Simple RM closing instructions}}
The following are suggestions for closing [[Wikipedia:Requested moves]] discussions. These should generally be applied only after the normal seven-day listing period has elapsed. These suggestions are addressed to formal move requests that occur on talk pages, i.e. ''controversial'' move requests, but are instructive as to the necessary page history investigation and preservation and cleanup procedures advisable upon any move. Requests listed in the [[Wikipedia:Requested moves/Technical requests|technical requests]] section can be simply removed after they have been processed. Where technical moves are contested, move the listing to the [[Wikipedia:Requested moves/Technical requests#Contested technical requests|contested technical requests section]].
Failure of an RM closer to follow the spirit and intent of these instructions, especially about [[#Determining consensus|properly weighing consensus with applicable policies and guidelines]], may result in the initiation of a '''[[WP:Move review|Move Review]]'''.
==Who can close requested moves==
===Conflicts of interest===
{{shortcut|WP:RMCOI}}
An [[WP:INVOLVED|involved editor]], whether an administrator [[WP:NACINV|or otherwise]], may '''not''' close a move request (with certain exceptions, detailed below). These criteria here are significantly stricter; this is intentional, befitting the less urgent nature of requested moves. You are considered involved if you have ever:
*opened a request to move the page;
*supported or opposed a request to move the page
*closed a previous controversial request to move the page
*[[WP:BOLD|boldly]] moved the page to a title you prefer (i.e., not including reverting [[WP:MOVEVANDAL|page move vandalism]] or performing a [[WP:RM/TR|technical request]])
*commented on any talk page in a way that has indicated a clear position on the specific move request
*edited the page in a way that demonstrates a clear position on the move request
An involved editor may comment in a move discussion, solicit a closure, or make a new move request at a later date, but may not close an open move request. If you are involved, trust the process and leave the closure to one of the many, many other editors on Wikipedia capable of closing move discussions.
After at least a week of discussion has taken place, you can solicit a closure by [[Wikipedia:Closure requests|requesting that an impartial editor assess consensus]]. The closer should be familiar with all relevant policies and guidelines, especially [[Wikipedia:Article titles|those relating to article titles]] and [[Wikipedia:Disambiguation|disambiguation]] and the procedures described on this page. Do not ask for a specific closer under any circumstances.
===Non-admin closure===
{{shortcut|WP:RMNAC}}
Experienced and uninvolved registered editors in good standing are encouraged to close requested move discussions. Any non-admin closure (NAC) must be explicitly declared with an appropriate template (such as {{tls|RMnac}}) placed directly after the reasoning for the close within the {{tls|RM top}} template (or use the {{para|nac}} parameter in the closing template).
Non-administrators are reminded that closing a discussion calls for an impartial assessment of consensus or lack thereof, although arguments supported by directly relevant policy and guidelines are given more weight (while keeping broader Wikipedia policy, guidelines, and consensus in mind). Any editor wishing to express an opinion on the requested move should join the discussion, not close it.
Anyone, administrator or not, who wishes to close a requested move must be very familiar with the policies and guidelines associated with it (especially [[Wikipedia:Article titles]], [[Wikipedia:Disambiguation]], and [[Wikipedia:Consensus]]), and ideally will have participated in several move requests previously. All closures of requested moves are subject to being taken to review at [[WP:Move review]] ([[WP:MRV]]), but assuming the criteria above are met, the mere fact that the closer was not an admin is not sufficient reason to reverse a closure. Indeed, many high-profile, controversial move requests have been closed as NACs, taken to [[WP:MRV]], and affirmed there. While non-admins should be cautious (as indeed all move closers should be) when closing discussions where significant contentious debate among participants is unresolved, any experienced and uninvolved editor in good standing may close any RM debate.
Occasionally, a move involves a redirect with multiple revisions, and requires technical intervention. Editors are permitted to close the discussion and file a [[WP:RMTM|technical move]] with a link to the closed discussion. The results of discussions in favor of moves should generally be respected by the administrator (or page mover). If an administrator notices a clearly improper move closure, they should revert the closure and re-open the discussion.
In any case where a non-admin closer does resort to a technical move request, the closer should actively monitor that request, and be ready and willing to perform all tidying after the move (as [[#Cleaning up after the move|instructed below]]), such as updating fair-use rationales and navbox links included on the page. If a non-admin closer is not willing to wait for the technical move and to perform the follow up in this manner, they should only close requested moves that do not require technical intervention.
====Closure by a page mover====
{{see|Wikipedia:Page mover|Template:RMnac}}
{{shortcut|WP:RMPMC}}
Editors with the page mover permission can perform certain technical moves without administrator assistance, such as a move over a redirect with more than one edit via a [[WP:PMVR#rr|round-robin]] page swap. A user is granted the page mover right after demonstrating a good understanding of the Wikipedia page naming system and decent page moving experience. Since closure by a page mover is a type of non-admin closure, it should be labeled as such using {{tls|RMnac}}, {{tls|RMpmc}} or equivalent (or use the {{para|pmc}} parameter in the closing template).
===Early closure===
{{shortcut|WP:RMEC}}
In general, move discussions should remain open for at least seven days (168 hours) to allow interested editors adequate time to participate. However, when no one has commented yet, or if opposition is unanimous, discussions may be closed prior to the seven-day timeframe for the following reasons:
* As long as no one has suggested any outcome besides ''not moving'', the ''proposer'' of a move may withdraw their request. The closure should say that the request was '''withdrawn'''.
* Furthermore, any editor may end such a move request with a '''procedural close''':
** if the request was initiated via [[Wikipedia:Blocking policy#Evasion and enforcement|block evasion]]. Under this circumstance, the closer may have been involved in the discussion; or
** to centralize related discussions that should have been a multi-page discussion.
* When the outcome of the move discussion is or has become almost certain and there is clearly no need to prolong discussion further, it may be closed early under the '''[[Wikipedia:Snowball clause|snowball clause]]'''. This clause should not be used to close a discussion when a particular outcome is merely "likely" or "highly likely", and there is a genuine and reasoned basis for disagreement. Move discussions are not a vote; it is important to be reasonably sure that there is little or no chance of accidentally excluding significant input or perspectives, or changing the weight of different views, if closed early. Especially, closers should beware of interpreting "early pile on" as necessarily showing how a discussion will end up. This can sometimes happen when a topic attracts high levels of attention from those engaged (or having a specific view) but slower attention from other less involved editors, perhaps with other points of view. It can sometimes be better to allow a few extra days even if the current discussion seems very clearly to hold one opinion, to avoid precluding significant input and as a courtesy to ensure that it really will be a snowball.
==Determining consensus==
{{shortcut|WP:RMCIDC}}
{{See also|Wikipedia:Advice on closing discussions}}
[[Wikipedia:Consensus|Consensus]] is determined not just by considering the preferences of the participants in a given discussion, but also by evaluating their arguments, assigning due weight accordingly, and giving due consideration to the relevant consensus of the Wikipedia community in general as reflected in applicable policy, guidelines and naming conventions.
{{shortcut|WP:RMNOMIN}}
'''{{vanchor|No minimum participation is required}}''' for requested moves. If no one has objected, go ahead and perform the move as requested, unless it is out of keeping with [[Wikipedia:Naming conventions|naming conventions]] or is otherwise in conflict with applicable guidelines or policy. Further, any move request that is out of keeping with [[Wikipedia:Naming conventions|naming conventions]] or is otherwise in conflict with applicable guideline and policy, unless there is a very good reason to [[WP:IAR|ignore rules]], should be closed without moving regardless of how many of the participants support it. Remember, the participants in any given discussion represent only a tiny fraction of the Wikipedia community whose consensus is reflected in the policy, guidelines and conventions to which all titles are to adhere. Thus, closers are expected to be familiar with such matters, so that they have the ability to make these assessments.
If objections have been raised, then the discussion should be evaluated just like any other discussion on Wikipedia: lack of consensus among participants along with no clear indication from policy and conventions normally means that no change happens (though like [[Articles for Deletion|WP:AFD]], this is not a vote and the quality of an argument is more important than whether it comes from a minority or a majority). However, sometimes a requested move is filed in response to a recent move from a long existing name that cannot be undone without administrative help. Therefore, if no consensus has been reached, the closer should move the article back to the most recent stable title. If no recent title has been stable, then the article should be moved to the title used by the first major contributor after the article ceased to be a stub.
{{shortcut|WP:RMNCREV}}{{anchor|RM no consensus revert}}
Note that according to {{section link|Wikipedia:Consensus|No consensus}}:
{{cquote|When article title discussions end without consensus, the [[Wikipedia:TITLECHANGES|applicable policy]] preserves the most recent stable title. If there is no prior stable title, then the default is the title used by the first major contributor after the article ceased to be a [[Wikipedia:Stub|stub]].}}
Therefore, if a page has been moved from a long-standing title, and it is not possible to move the page back to its original title during the discussion, the default title will be the title prior to the contested move. For example, if an article is created at [[Soda can]] and stays there for years prior to being [[WP:BOLD]]ly moved to [[Pop can]], and a move request is filed leading to a decision of "no consensus", the article must be moved back to its longstanding title. This is the case even if the ''original'' page was placed at [[Pop can]] or [[Fizzy drink can]] or [[Orange-flavored soft drink can]], as long as [[Soda can]] took over through consensus and can be determined to be the actual long-standing title.
===Relisting===
{{For|the instructions on relisting move discussions|Wikipedia:Requested moves#Relisting a requested move}}
Relisting is an option when a discussion cannot otherwise be closed, usually due to lack of consensus. Editors are under no obligation to wait to close a move request after it is relisted. Once a move request has been open for the full seven days, it may be closed at any time by an uninvolved editor. Closers wait mainly for general agreement, for consensus, to emerge.
'''Exception:''' if a page is added while relisting. Sometimes editors may not realize that to move page A to B, page B must first be moved to C. When a new page move request is added to one that is already seven days old, the RMCD bot places the notification tag on the newly added page that day, and the discussion must go a full seven days (more) before being closed. It is helpful to closers when relisters leave a note just below the nomination that this happened.
====Relisting and participating====
A relister may later become a participant or closer in the requested move discussion or survey.
===Only move involved pages===
A move request about moving X to Y should never be closed in such a way as to require page Z to move if Z wasn't listed in the original move request (see [[#Moves of other pages|below]]). <!--This rule has been in place, in one way or another, since 2014. Please do not remove without discussion.-->
Where a discussion would result in the original title pointing to a "Foo (disambiguation)" page, the practice of pagemovers has been to immediately move the disambiguation page to the base page name, per [[WP:MALPLACED]]. This is because a disambiguation page is presumed to belong at the base page name unless that title is taken by a primary topic.
==<span class="anchor" id="Three possible outcomes"></span>Write a clear, concise closing statement==
<!-- This Anchor tag serves to provide a permanent target for incoming section links. Please do not remove it, nor modify it, except to add another appropriate anchor. If you modify the section title, please anchor the old title. It is always best to anchor an old section header that has been changed so that links to it will not be broken. See [[Template:Anchor]] for details. This template is {{subst:Anchor comment}} -->
{{shortcut|WP:THREEOUTCOMES}}
{{further|#Determining consensus}}
There are generally three different outcomes for consensus in requested moves. The closer should clearly show which outcome has taken place so that other editors may quickly see the progression of consensus regarding the title; it is much easier to move an article that has never had a firm consensus about the title.
# '''Not moved''' (or '''consensus not to move''') should be used when a consensus ''has'' formed to keep the current title and ''not'' rename the article(s) in question. For instance, a proposal to rename [[Bob Dylan]] to [[Squeezy Joe]] would likely result in everyone (or nearly everyone) agreeing that the proposed move should not take place; this notifies other editors that they should probably not propose this move in the future until and unless circumstances change. There is a positive consensus found, and that consensus is for the page to stay exactly where it is.
# '''No consensus''' should be used when there is neither a consensus to move ''nor'' a consensus to keep the current title. This may be because a discussion has fractured into several possible titles and none seem especially suitable, or simply because equally strong arguments and appeals to Wikipedia policy and outside sources were found on both sides, without any clear reason to move the page found in the discussion. Of course, as elsewhere on Wikipedia, this usually means that no action is to be taken at the present time.
# '''Moved''' (or '''consensus to move''') should be used when consensus ''is'' found to rename the page. If there is any question whatsoever as to ''which'' title it should be moved to, please note this in the closing summary (e.g. "'''moved to [[Squeezy José]]'''"). This almost always sets a consensus for the new title, and further requests to move the page are likely to fail unless new information or arguments are brought forth.
While it is usually bad form to re-request a move if consensus ''is'' found against it (until and unless circumstances change), it is ''not'' considered bad form to re-raise a request that found "no consensus" to move. (Successful move re-requests generally, though not always, take place at least three months after the previous one. An exception is when the no-consensus move discussion suggests a clear, new course of action.)
===Discussions involving multiple options===
{{seealso|WP:Bartender}}
{{shortcut|WP:NOGOODOPTIONS|WP:OTHEROPTIONS}}
Most move requests are simple. Alice proposes that we move X to Y. Bob, Carol and Dave chime in. Edgar analyzes the discussion and decides whether there is a consensus, and then gives one of the three outcomes above.
But sometimes things get complicated. Alice proposes moving X to Y, but then Bob raises real concerns about Y, and proposes Z instead. Carol says, no, we should stay with Y. Dave says we actually need to keep the article at X. Edgar shows up and is very confused. What does he do?
If you as a closer are in doubt because too many titles have been proposed and there's no real consensus anywhere, it is generally best to '''close as no consensus''' and allow someone to re-propose the move to a more specific or better title.
{{anchor|NOTCURRENTTITLE}}{{shortcut|WP:NOTCURRENTTITLE|WP:NOTCURRENT}}
But then again, there are situations where multiple names have been proposed and no consensus arises out of any, except that it '''is''' determined that the current title should ''not'' host the article. (There are good arguments for Y, and there are good arguments for Z, but there are virtually ''no'' good arguments for it to stay at X.) In these difficult circumstances, the closer should pick the best title of the options available, and then make clear that while consensus has rejected the former title (and no request to bring it back should be made lightly), there is no consensus for the title actually chosen. Because such closures are inherently complex and require the closer to use more independent judgment than normal, it is ''strongly encouraged'' to provide an explicit closing statement in such closures. As this result does not indicate a consensus for the chosen title, anyone who objects to the closer's decision may make another move request ''at any time'', and is advised to create such a request instead of taking the closure to [[WP:MRV|move review]].
If a result of the NOGOODOPTIONS close would be the disambiguation of a title that was not included in the original proposal, and for which there has been no notice on its corresponding Talk page, then instead of closing, a notice should be placed on that article's talk page, and the proposal relisted. No title should be disambiguated as a result of an RM without notice on its Talk page.
==Closing the requested move==
When you complete an entry on the project (whether the move was accepted or rejected), '''remove the {{tl|requested move/dated}} tag from the talk page''', or change {{tla|requested move/dated|requested move/'''dated'''}} to <nowiki>{{</nowiki>[[WP:Subst|'''subst''']]:[[Template:Requested move/end|requested move/'''end''']]}}. You should also add and sign a comment to indicate whether the move was accepted or rejected in the discussion area for the requested move. To ease this process, there are several scripts that can help; the [[Wikipedia:User scripts/List#Closing discussions|most popular]] version that is actively maintained being [[User:BilledMammal/Move+]]. Alternatives include [[User:DannyS712/PageMoverClosure]] and [[User:Andy M. Wang/closeRM]].
While historically, there have been other options for closing the move request survey on the affected article's talk page, nowadays we exclusively use the twin templates {{tlxs|RM top|'''result of the discussion'''.}} and {{tlxs|RM bottom}}.
===Step-by-step closing procedure===
After clicking the [edit] tab next to the move discussion, you may follow these step-by-step instructions for closing an RM discussion:
{|class="wikitable" style="font-size: 90%; text-align: center; width: 100%;"
|-
!Before closing
!After closing
!Description
|-
| style="background-color: lightgray"|<nowiki>==Requested move==</nowiki>
| style="background-color: lightgray"|<nowiki>==Requested move==</nowiki>
|Leave the heading alone; the close starts below it.
|-
| style="background-color: yellow"|<nowiki>{{Requested move/dated|Foo}}</nowiki>
| style="background-color: lightgreen"|<nowiki>{{subst:RM top|'''RESULT.'''</nowiki><span class="anonymous-show extendedconfirmed-show">{{!}}<span class="extendedmover-show">pm</span>nac=yes</span><nowiki>}}</nowiki>
|Replace text on left with text on right.
|-
|style="background-color: lightgray"|''DISCUSSION''
|style="background-color: lightgray"|''DISCUSSION''
|Body of the discussion stays unchanged.
|-
|
| style="background-color: lightgreen;"|<nowiki>{{subst:RM bottom}}</nowiki>
|Add the bottom template.
|-
|}
*If additional explanation is provided as to why you have closed the move discussion as a certain result, add additional comments immediately after '''<nowiki>'''RESULT'''.</nowiki>'''.
*Save the page with an edit summary such as "<code>Closing requested move survey; page moved/not moved</code>".
After closing, the page should look similar to this:
<div class="boilerplate mw-archivedtalk" style="background-color: var(--background-color-success-subtle, #efe); color: var(--color-base, #000); margin: 0; padding: 0 10px 0 10px; border: 1px dotted var(--border-color-subtle, #AAAAAA);"><!-- Template:RM top -->
:''The following is a closed discussion of a [[Wikipedia:Requested moves|requested move]]. <span style="color: var(--color-error, red);">'''Please do not modify it.'''</span> Subsequent comments should be made in a new section on the talk page. Editors desiring to contest the closing decision should consider a [[Wikipedia:move review|move review]] after discussing it on the closer's talk page. No further edits should be made to this discussion.''
The result of the move request was: '''RESULT'''. [Additional comments]. <span class="anonymous-show extendedconfirmed-show"><small>([[Wikipedia:Requested moves/Closing instructions#Non-admin closure|non-admin closure]])</small> </span>[[User:Example|Example]] ([[User talk:Example|talk]]) {{CURRENTTIME}}, {{CURRENTDAY}} {{CURRENTMONTHNAME}} {{CURRENTYEAR}} (UTC)
----
[[Foo]] → {{no redirect|1=Foobar}} – rationale of nominator. [[User:Example|Example]] ([[User talk:Example|talk]]) {{CURRENTTIME}}, {{CURRENTDAY}} {{CURRENTMONTHNAME}} {{CURRENTYEAR}} (UTC)
*'''Supports/Opposes''' with discussion
{{RM bottom}}
{{hidden end}}
</div>
===Add <span class="nowrap">{{</span>[[Template:Old moves|Old moves]]<span class="nowrap">}}</span> to the talk page if so desired===
After the move is complete, the {{tl|Old moves}} template may be added to the top of the talk page (or updated if already present), allowing editors to see previous move discussions that might otherwise be archived. This is helpful for titles that are likely to be challenged again, so that any would-be re-proposer can make reference to previous arguments and consider how a consensus may be formed to move. In the case of pages with multiple move discussions, these templates should always be added/updated after the closure of an RM.
===Automatic removals===
Once the article's talk page has been updated, there's no need to return to the [[Wikipedia:Requested moves]] page and delete the article's entry there; this will be performed automatically by a bot.
Likewise, {{u|RMCD bot}} will remove the {{tl|Title notice}} from the page itself within 15 minutes.
===Using move protection during RMs and immediately after RM closes===
{{see also|WP:MOVP}}
Some RM discussions are contentious; undiscussed, unilateral page moves during a discussion or page moves made immediately after and contrary to an RM close decision are disruptive and hurt the integrity of the RM process. Admins monitoring RM discussions should use their discretion to ''move protect'' articles during contentious RM discussions when they believe a premature, undiscussed unilateral move would be disruptive to the discussion. The same discretion should be used to start or continue move protection immediately after the RM close. Generally, such move protection should be limited to no more than 30 days under normal circumstances. The RM closing comment should reference the move protection.
== Moving procedures ==
===Edit history of destination page===
{{main|Wikipedia:Moving a page#Swapping two pages|Wikipedia:Page mover#Round-robin page moves}}
The majority of target names for move requests already exist as [[WP:REDIRECT|redirects]] to the present names. Whether a redirect or otherwise, that existing target title should be investigated to see whether it has a minor or major [[Help:Page history|page history]]. If it has a ''minor'' page history, generally meaning it only existed as a redirect, and was never a duplicate article, never had content that was cut and pasted to the present title, nor merged there, ''it may simply be deleted.'' However, if the target page title has a ''major'' history it should '''never''' be simply deleted, as [[WP:CWW|we need to retain such page histories for proper copyright attribution]]. There are three ways to deal with target pages with major histories, dependent on circumstances:
#For page histories resulting from cut and paste moves, the ''correct'' way to fix this is to merge the page history of the present article and the redirect, using the procedure outlined at [[Wikipedia:History merging]]. On rare occasions, this procedure will not work correctly. Once a history merge is done, it cannot easily be undone, so don't pick this option unless it is definitely the right one. You can request history merges at [[Wikipedia:Requests for history merge]].
# For duplicate articles and merged content, or alternatively for cut and paste moves, the page histories of the article and the redirect can be swapped with a round-robin move. For cut and paste moves this leaves a bifurcated history, but has less chance of causing problems. Simply move the redirect with a major history to a temporary name (<code>Draft:Move/''NAME''</code> is suggested), suppressing the creation of a redirect (in the event you forget to suppress the redirect, delete the same); next, move the article to the move target location, again suppressing the redirect and deleting the same if you forget; next, move the redirect from its temporary location to the title at which the article you just moved was formerly located. At this point the redirect will be pointing at itself; re-target it to point to the new location of the article.
# Another option is for redirect pages with major histories to be archived into a talk namespace, and a link then placed on the article's [[Wikipedia:talk page|talk page]]. (An example of such a page is at [[Talk:Network SouthEast]], which was originally created as a duplicate article at [[Network SouthEast]] and later archived, when the original article was moved from [[Network South East]]).
===Cleaning up after the move===
You should not close any move discussion if you are unwilling to do the necessary clean up tasks.{{#lsth:Wikipedia:Moving a page|<span class="anchor" id="POST"></span><span class="anchor" id="Post-move cleanup"></span>Cleaning up after a move}}
===<span class="anchor" id="NOTOTHERPAGES"></span>Moves of other pages===
{{shortcut|WP:NOTOTHERPAGES}}
If a page is to be moved as the result of a move request, mention should be made of this in the move proposal and a notice should be placed on the talk page of the article to be moved (unless of course it is hosting the discussion). Generally, a move request on whether to move [[X]] to [[Y]] should have no impact on page [[Z]]'s title, unless it is initiated as a {{tl|multi-move request}} that mentions moving Z as a possibility. This is because the editors most interested and aware of Z are not able to contribute their expertise to the naming discussion, since it's happening at a different place without any notice given.
These situations often come up when [[Foo (barge)]] is proposed to move to, say, [[Foo (enormous sailing thing)]], and someone mentions that they think the barge is actually the primary topic. A consensus of these barge enthusiasts may then informally suggest that the existing article [[Foo]] be moved to [[Foo (bar)]], without actually notifying Foobar-interested editors by signaling at [[Talk:Foo]] that a move request involving that page is taking place. This often leads to strife and another, more contentious move request. If consensus at X signals that Z should be disambiguated, relist the move proposal and leave notice at [[Talk:Z]].
Even if local consensus is clear, or when following [[WP:NOGOODOPTIONS]], when closing a move request '''do not disambiguate article titles for which there has been no notice on the corresponding Talk page'''. Disambiguating is always potentially controversial. Again, in these cases relist the move request and post a notice on the to-be-disambiguated article's Talk page. Such notice is ''not'' required when an article is being moved to the base name of its current disambiguated title.
[[Category:Requested moves| ]]
[[Category:Administrator instructions]]
en5wmq9535c09j9mmj0nk40rvhmdkko
हैगियोग्राफी
0
101026
797597
2026-06-09T06:52:55Z
SM7
3953
पन्ना बनावल गइल "'''हैगियोग्राफी''' ({{Langx|en|hagiography}})<ref>{{cite OED |hagiography}}</ref> कवनो संत, धार्मिक नेता भा आध्यात्मिक महापुरुष के जीवनी-लेखन के कहल जाला। व्यापक अर्थ में ई दुनिया के कवनो भी धर्म में उपदेशक,..." के साथ
797597
wikitext
text/x-wiki
'''हैगियोग्राफी''' ({{Langx|en|hagiography}})<ref>{{cite OED |hagiography}}</ref> कवनो संत, धार्मिक नेता भा आध्यात्मिक महापुरुष के जीवनी-लेखन के कहल जाला। व्यापक अर्थ में ई दुनिया के कवनो भी धर्म में उपदेशक, पुजारी, धर्म संस्थापक, संत, साधु, साध्वी भा पूजनीय धार्मिक व्यक्तित्व के प्रशंसात्मक आ आदर्शीकृत जीवन-वृत्तांत हो सकेला।<ref name="MongeChirico2016">{{cite book|author=Rico G. Monge|editor=Rico G. Monge, Kerry P. C. San Chirico and Rachel J. Smith|title=Hagiography and Religious Truth: Case Studies in the Abrahamic and Dharmic Traditions|url=https://books.google.com/books?id=nDnCDAAAQBAJ| year=2016|publisher=Bloomsbury Publishing|isbn=978-1474235792|pages=7–22}}</ref><ref>{{cite book|author=Jeanette Blonigen Clancy|title=Beyond Parochial Faith: A Catholic Confesses|url=https://books.google.com/books?id=efCaDwAAQBAJ&pg=PA137 |year=2019|publisher=Wipf and Stock Publishers|isbn=978-1532672828|page=137}}</ref><ref>{{cite book | last=Rapp | first=Claudia | title=Byzantine Religious Culture | chapter=Hagiography and the Cult of Saints in the Light of Epigraphy and Acclamations | publisher=Brill Academic| year=2012 | pages=289–311 | isbn=978-9004226494 | doi=10.1163/9789004226494_017 }}</ref>
प्रारंभिक [[ईसाई धर्म|ईसाई परंपरा]] में हैगियोग्राफी के रूप कई प्रकार के मिले लें। इनहन में संत लोगन के जिनगी-कथा — ''वीटा'' (''vita'') , जेकर अर्थ "जीवन" होला आ जवन अधिकांश मध्यकालीन जीवनी के शीर्षक के शुरुआत में मिलेला — संत द्वारा कइल गइल कार्य आ चमत्कारन के वर्णन, उनका शहादत भा बलिदान के विवरण (जेकरा के ''पैसियो'' कहल जाला), भा एह सभ के मिश्रित रूप शामिल रहे ला। ईसाई धर्म के अलावे अइसन लेखन [[बौद्ध धर्म]],<ref>Jonathan Augustine (2012), ''Buddhist Hagiography in Early Japan'', Routledge, {{ISBN|978-0415646291}}</ref> [[हिंदू धर्म]]<ref>David Lorenzen (2006), ''Who Invented Hinduism?'', [[Yoda Press]], {{ISBN|978-8190227261}}, pp. 120–121</ref> ताओ धर्म, सिख धर्म आ इस्लाम में भी मिले ला। एह प्रकार के रचना सभ के मुख्य उद्देश्य खाली ऐतिहासिक जानकारी देहल ना, बल्कि संत भा धार्मिक व्यक्तित्व के आदर्श जीवन, आध्यात्मिक महत्त्व आ धार्मिक प्रभाव के उजागर कइल भी होला।
[[इतिहास]] के आधुनिक बिद्वान लोग अइसन जीवनी-लेखन के कुछ हीन नजरिया से देके ला काहें से कि इनहन में लेखक अपना बिसाय के प्रति कुछ बेसिए तारीफी भा आदर के झुकाव लिहले रहे ला, तटस्थ ना होला।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
jp32pn5b157bv4kaa6gbo48t00csqjp
797598
797597
2026-06-09T06:57:31Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा +[[श्रेणी:इतिहास लेखन]]; +[[श्रेणी:जीवनी]]; +[[श्रेणी:मध्यकालीन इतिहास]]
797598
wikitext
text/x-wiki
'''हैगियोग्राफी''' ({{Langx|en|hagiography}})<ref>{{cite OED |hagiography}}</ref> कवनो संत, धार्मिक नेता भा आध्यात्मिक महापुरुष के जीवनी-लेखन के कहल जाला। व्यापक अर्थ में ई दुनिया के कवनो भी धर्म में उपदेशक, पुजारी, धर्म संस्थापक, संत, साधु, साध्वी भा पूजनीय धार्मिक व्यक्तित्व के प्रशंसात्मक आ आदर्शीकृत जीवन-वृत्तांत हो सकेला।<ref name="MongeChirico2016">{{cite book|author=Rico G. Monge|editor=Rico G. Monge, Kerry P. C. San Chirico and Rachel J. Smith|title=Hagiography and Religious Truth: Case Studies in the Abrahamic and Dharmic Traditions|url=https://books.google.com/books?id=nDnCDAAAQBAJ| year=2016|publisher=Bloomsbury Publishing|isbn=978-1474235792|pages=7–22}}</ref><ref>{{cite book|author=Jeanette Blonigen Clancy|title=Beyond Parochial Faith: A Catholic Confesses|url=https://books.google.com/books?id=efCaDwAAQBAJ&pg=PA137 |year=2019|publisher=Wipf and Stock Publishers|isbn=978-1532672828|page=137}}</ref><ref>{{cite book | last=Rapp | first=Claudia | title=Byzantine Religious Culture | chapter=Hagiography and the Cult of Saints in the Light of Epigraphy and Acclamations | publisher=Brill Academic| year=2012 | pages=289–311 | isbn=978-9004226494 | doi=10.1163/9789004226494_017 }}</ref>
प्रारंभिक [[ईसाई धर्म|ईसाई परंपरा]] में हैगियोग्राफी के रूप कई प्रकार के मिले लें। इनहन में संत लोगन के जिनगी-कथा — ''वीटा'' (''vita'') , जेकर अर्थ "जीवन" होला आ जवन अधिकांश मध्यकालीन जीवनी के शीर्षक के शुरुआत में मिलेला — संत द्वारा कइल गइल कार्य आ चमत्कारन के वर्णन, उनका शहादत भा बलिदान के विवरण (जेकरा के ''पैसियो'' कहल जाला), भा एह सभ के मिश्रित रूप शामिल रहे ला। ईसाई धर्म के अलावे अइसन लेखन [[बौद्ध धर्म]],<ref>Jonathan Augustine (2012), ''Buddhist Hagiography in Early Japan'', Routledge, {{ISBN|978-0415646291}}</ref> [[हिंदू धर्म]]<ref>David Lorenzen (2006), ''Who Invented Hinduism?'', [[Yoda Press]], {{ISBN|978-8190227261}}, pp. 120–121</ref> ताओ धर्म, सिख धर्म आ इस्लाम में भी मिले ला। एह प्रकार के रचना सभ के मुख्य उद्देश्य खाली ऐतिहासिक जानकारी देहल ना, बल्कि संत भा धार्मिक व्यक्तित्व के आदर्श जीवन, आध्यात्मिक महत्त्व आ धार्मिक प्रभाव के उजागर कइल भी होला।
[[इतिहास]] के आधुनिक बिद्वान लोग अइसन जीवनी-लेखन के कुछ हीन नजरिया से देके ला काहें से कि इनहन में लेखक अपना बिसाय के प्रति कुछ बेसिए तारीफी भा आदर के झुकाव लिहले रहे ला, तटस्थ ना होला।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:इतिहास लेखन]]
[[श्रेणी:जीवनी]]
[[श्रेणी:मध्यकालीन इतिहास]]
g9yjsxl8hchif6guw3fcmkic42u75go
797599
797598
2026-06-09T06:58:45Z
SM7
3953
[[User:SM7/stubsorter|Stubsorter]] के मदद से {{Hist-stub}} जोड़ल गइल।
797599
wikitext
text/x-wiki
'''हैगियोग्राफी''' ({{Langx|en|hagiography}})<ref>{{cite OED |hagiography}}</ref> कवनो संत, धार्मिक नेता भा आध्यात्मिक महापुरुष के जीवनी-लेखन के कहल जाला। व्यापक अर्थ में ई दुनिया के कवनो भी धर्म में उपदेशक, पुजारी, धर्म संस्थापक, संत, साधु, साध्वी भा पूजनीय धार्मिक व्यक्तित्व के प्रशंसात्मक आ आदर्शीकृत जीवन-वृत्तांत हो सकेला।<ref name="MongeChirico2016">{{cite book|author=Rico G. Monge|editor=Rico G. Monge, Kerry P. C. San Chirico and Rachel J. Smith|title=Hagiography and Religious Truth: Case Studies in the Abrahamic and Dharmic Traditions|url=https://books.google.com/books?id=nDnCDAAAQBAJ| year=2016|publisher=Bloomsbury Publishing|isbn=978-1474235792|pages=7–22}}</ref><ref>{{cite book|author=Jeanette Blonigen Clancy|title=Beyond Parochial Faith: A Catholic Confesses|url=https://books.google.com/books?id=efCaDwAAQBAJ&pg=PA137 |year=2019|publisher=Wipf and Stock Publishers|isbn=978-1532672828|page=137}}</ref><ref>{{cite book | last=Rapp | first=Claudia | title=Byzantine Religious Culture | chapter=Hagiography and the Cult of Saints in the Light of Epigraphy and Acclamations | publisher=Brill Academic| year=2012 | pages=289–311 | isbn=978-9004226494 | doi=10.1163/9789004226494_017 }}</ref>
प्रारंभिक [[ईसाई धर्म|ईसाई परंपरा]] में हैगियोग्राफी के रूप कई प्रकार के मिले लें। इनहन में संत लोगन के जिनगी-कथा — ''वीटा'' (''vita'') , जेकर अर्थ "जीवन" होला आ जवन अधिकांश मध्यकालीन जीवनी के शीर्षक के शुरुआत में मिलेला — संत द्वारा कइल गइल कार्य आ चमत्कारन के वर्णन, उनका शहादत भा बलिदान के विवरण (जेकरा के ''पैसियो'' कहल जाला), भा एह सभ के मिश्रित रूप शामिल रहे ला। ईसाई धर्म के अलावे अइसन लेखन [[बौद्ध धर्म]],<ref>Jonathan Augustine (2012), ''Buddhist Hagiography in Early Japan'', Routledge, {{ISBN|978-0415646291}}</ref> [[हिंदू धर्म]]<ref>David Lorenzen (2006), ''Who Invented Hinduism?'', [[Yoda Press]], {{ISBN|978-8190227261}}, pp. 120–121</ref> ताओ धर्म, सिख धर्म आ इस्लाम में भी मिले ला। एह प्रकार के रचना सभ के मुख्य उद्देश्य खाली ऐतिहासिक जानकारी देहल ना, बल्कि संत भा धार्मिक व्यक्तित्व के आदर्श जीवन, आध्यात्मिक महत्त्व आ धार्मिक प्रभाव के उजागर कइल भी होला।
[[इतिहास]] के आधुनिक बिद्वान लोग अइसन जीवनी-लेखन के कुछ हीन नजरिया से देके ला काहें से कि इनहन में लेखक अपना बिसाय के प्रति कुछ बेसिए तारीफी भा आदर के झुकाव लिहले रहे ला, तटस्थ ना होला।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:इतिहास लेखन]]
[[श्रेणी:जीवनी]]
[[श्रेणी:मध्यकालीन इतिहास]]
{{Hist-stub}}
q97dzgy0y6no14ldq515ce9kilww2eg
अस्सी घाट
0
101027
797604
2026-06-09T09:03:20Z
SM7
3953
नया आधार लेख
797604
wikitext
text/x-wiki
'''अस्सी घाट''' भारत के [[बनारस]] शहर में स्थित [[बनारस के घाट|घाटन]] में सबसे दक्खिनी माथ पर स्थित घाट हवे। बनारस आवे वाला बहुत लोग खातिर ई घाट खास पहिचान रखे ला, काहे कि एह इलाका में लंबा समय तक रहे वाला विदेशी विद्यार्थी, रिसर्चर आ पर्यटक लोग के संख्या बेसी रहेला।
अस्सी घाट पर हर बिहान "सुबहे-बनारस" नाम के सांस्कृतिक आ आध्यात्मिक कार्यक्रम आयोजित कइल जाला। एह कार्यक्रम में योग, ध्यान, वैदिक मंत्रोच्चार, भजन-कीर्तन आ सांस्कृतिक प्रस्तुति सभ शामिल रहेली, जे बनारस के धार्मिक आ सांस्कृतिक परंपरा के जीवंत रूप प्रस्तुत करे लीं।
ar1dje3dclx4nvbe3jv696hjr2oy00m
797605
797604
2026-06-09T09:03:38Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:बनारस के घाट]] जोड़ल गइल
797605
wikitext
text/x-wiki
'''अस्सी घाट''' भारत के [[बनारस]] शहर में स्थित [[बनारस के घाट|घाटन]] में सबसे दक्खिनी माथ पर स्थित घाट हवे। बनारस आवे वाला बहुत लोग खातिर ई घाट खास पहिचान रखे ला, काहे कि एह इलाका में लंबा समय तक रहे वाला विदेशी विद्यार्थी, रिसर्चर आ पर्यटक लोग के संख्या बेसी रहेला।
अस्सी घाट पर हर बिहान "सुबहे-बनारस" नाम के सांस्कृतिक आ आध्यात्मिक कार्यक्रम आयोजित कइल जाला। एह कार्यक्रम में योग, ध्यान, वैदिक मंत्रोच्चार, भजन-कीर्तन आ सांस्कृतिक प्रस्तुति सभ शामिल रहेली, जे बनारस के धार्मिक आ सांस्कृतिक परंपरा के जीवंत रूप प्रस्तुत करे लीं।
[[श्रेणी:बनारस के घाट]]
argxbsory3l9vm87ony12o44rnbjsn6
797606
797605
2026-06-09T09:04:26Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:बनारस]] जोड़ल गइल
797606
wikitext
text/x-wiki
'''अस्सी घाट''' भारत के [[बनारस]] शहर में स्थित [[बनारस के घाट|घाटन]] में सबसे दक्खिनी माथ पर स्थित घाट हवे। बनारस आवे वाला बहुत लोग खातिर ई घाट खास पहिचान रखे ला, काहे कि एह इलाका में लंबा समय तक रहे वाला विदेशी विद्यार्थी, रिसर्चर आ पर्यटक लोग के संख्या बेसी रहेला।
अस्सी घाट पर हर बिहान "सुबहे-बनारस" नाम के सांस्कृतिक आ आध्यात्मिक कार्यक्रम आयोजित कइल जाला। एह कार्यक्रम में योग, ध्यान, वैदिक मंत्रोच्चार, भजन-कीर्तन आ सांस्कृतिक प्रस्तुति सभ शामिल रहेली, जे बनारस के धार्मिक आ सांस्कृतिक परंपरा के जीवंत रूप प्रस्तुत करे लीं।
[[श्रेणी:बनारस के घाट]]
[[श्रेणी:बनारस]]
920osmt6pu0zlf33yk50xl20ml7fpso
797607
797606
2026-06-09T09:05:04Z
SM7
3953
+ navigation template
797607
wikitext
text/x-wiki
'''अस्सी घाट''' भारत के [[बनारस]] शहर में स्थित [[बनारस के घाट|घाटन]] में सबसे दक्खिनी माथ पर स्थित घाट हवे। बनारस आवे वाला बहुत लोग खातिर ई घाट खास पहिचान रखे ला, काहे कि एह इलाका में लंबा समय तक रहे वाला विदेशी विद्यार्थी, रिसर्चर आ पर्यटक लोग के संख्या बेसी रहेला।
अस्सी घाट पर हर बिहान "सुबहे-बनारस" नाम के सांस्कृतिक आ आध्यात्मिक कार्यक्रम आयोजित कइल जाला। एह कार्यक्रम में योग, ध्यान, वैदिक मंत्रोच्चार, भजन-कीर्तन आ सांस्कृतिक प्रस्तुति सभ शामिल रहेली, जे बनारस के धार्मिक आ सांस्कृतिक परंपरा के जीवंत रूप प्रस्तुत करे लीं।
{{बनारस}}
[[श्रेणी:बनारस के घाट]]
[[श्रेणी:बनारस]]
rtd43vsr27h7b1qaag4m69zrn6djymo